LeetCode30
LeetCode30 串聯所有單詞的子串
hashmap何時使用
在涉及字串S是否在字串P中被包含,或者尋找特定字元,只要是找值,或者是要求是否符合某個特徵,都可以用hashmap。
那麼本題利用hashmap更是一種巧妙地方法。演算法如下:
建立兩個hashmap,一個儲存單詞,一個儲存模式串中子串中單詞出現個數。
將所有單詞放到hashmap1中,並記錄所有單詞出現的次數。
接下來對子串進行掃描:
如果字串不出現在hashmap1中,則跳過進行下一輪迴圈,
如果在hashmap1中,放到hashmap2中,如果hashmap2與hashmap1中存放的內容一致時,即可滿足答案。
本題注意單詞長度相同。
解法一:
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<Integer>();
int wordNum = words.length;
if (wordNum == 0) {
return res;
}
int wordLen = words[0].length();
//HashMap1 存所有單詞
HashMap<String, Integer> allWords = new HashMap<String, Integer>();
for (String w : words) {
int value = allWords.getOrDefault(w, 0);
allWords.put(w, value + 1);
}
//遍歷所有子串
for (int i = 0; i < s.length() - wordNum * wordLen + 1; i++) {
//HashMap2 存當前掃描的字串含有的單詞
HashMap<String, Integer> hasWords = new HashMap<String, Integer>();
int num = 0;
//判斷該子串是否符合
while (num < wordNum) {
String word = s.substring(i + num * wordLen, i + (num + 1) * wordLen);
//判斷該單詞在 HashMap1 中
if (allWords.containsKey(word)) {
int value = hasWords.getOrDefault(word, 0);
hasWords.put(word, value + 1);
//判斷當前單詞的 value 和 HashMap1 中該單詞的 value
if (hasWords.get(word) > allWords.get(word)) {
break;
}
} else {
break;
}
num++;
}
//判斷是不是所有的單詞都符合條件
if (num == wordNum) {
res.add(i);
}
}
return res;
}
解法二:
解法二是對解法一的優化,因為解法一每次只是移動一個字元,浪費效率,
所以解法二每次移動單詞長度個字元。
但是要分情況進行討論:
請參考以下文章:
https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-6/
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<Integer>();
int wordNum = words.length;
if (wordNum == 0) {
return res;
}
int wordLen = words[0].length();
HashMap<String, Integer> allWords = new HashMap<String, Integer>();
for (String w : words) {
int value = allWords.getOrDefault(w, 0);
allWords.put(w, value + 1);
}
//將所有移動分成 wordLen 類情況
for (int j = 0; j < wordLen; j++) {
HashMap<String, Integer> hasWords = new HashMap<String, Integer>();
int num = 0; //記錄當前 HashMap2(這裡的 hasWords 變數)中有多少個單詞
//每次移動一個單詞長度
for (int i = j; i < s.length() - wordNum * wordLen + 1; i = i + wordLen) {
boolean hasRemoved = false; //防止情況三移除後,情況一繼續移除
while (num < wordNum) {
String word = s.substring(i + num * wordLen, i + (num + 1) * wordLen);
if (allWords.containsKey(word)) {
int value = hasWords.getOrDefault(word, 0);
hasWords.put(word, value + 1);
//出現情況三,遇到了符合的單詞,但是次數超了
if (hasWords.get(word) > allWords.get(word)) {
// hasWords.put(word, value);
hasRemoved = true;
int removeNum = 0;
//一直移除單詞,直到次數符合了
while (hasWords.get(word) > allWords.get(word)) {
String firstWord = s.substring(i + removeNum * wordLen, i + (removeNum + 1) * wordLen);
int v = hasWords.get(firstWord);
hasWords.put(firstWord, v - 1);
removeNum++;
}
num = num - removeNum + 1; //加 1 是因為我們把當前單詞加入到了 HashMap 2 中
i = i + (removeNum - 1) * wordLen; //這裡依舊是考慮到了最外層的 for 迴圈,看情況二的解釋
break;
}
//出現情況二,遇到了不匹配的單詞,直接將 i 移動到該單詞的後邊(但其實這裡
//只是移動到了出現問題單詞的地方,因為最外層有 for 迴圈, i 還會移動一個單詞
//然後剛好就移動到了單詞後邊)
} else {
hasWords.clear();
i = i + num * wordLen;
num = 0;
break;
}
num++;
}
if (num == wordNum) {
res.add(i);
}
//出現情況一,子串完全匹配,我們將上一個子串的第一個單詞從 HashMap2 中移除
if (num > 0 && !hasRemoved) {
String firstWord = s.substring(i, i + wordLen);
int v = hasWords.get(firstWord);
hasWords.put(firstWord, v - 1);
num = num - 1;
}
}
}
return res;
}