給定一個字符串 s 和一些長度相同的單詞 words。在 s 中找出可以恰好串聯 words 中所有單詞的子串的起始位置。
注意子串要與 words 中的單詞完全匹配,中間不能有其他字符,但不需要考慮 words 中單詞串聯的順序。
示例 1:
輸入:
s = "barfoothefoobarman",
words = ["foo","bar"]
輸出: [0,9]
解釋: 從索引 0 和 9 開始的子串分別是 "barfoor" 和 "foobar" 。
輸出的順序不重要, [9,0] 也是有效答案。
示例 2:
輸入:
s = "wordgoodstudentgoodword",
words = ["word","student"]
輸出: []
解法
這道題有一個很關鍵的條件長度相同的單詞,單詞長度一致這個題可以退化為單詞長度為1來考慮。問題變成:給定一個字符串,尋找由字母集words組成的字串。這種字符串問題就可以考慮使用一個滑窗來解決,掃描源字符串,如果新增單詞是單詞集words中的單詞,並且該單詞未在滑窗中出現滑窗增漲(滑窗right增加),否則滑窗縮小(滑窗left增加)。當滑窗滿足需求時,記錄滑窗左側(left)的位置,繼續掃描,直至無法在生成滿足需求的滑窗為止。
代碼
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> result = new ArrayList<>();
if (s == null || s.length() == 0 || words == null || words.length == 0) {
return result;
}
Map<String, Integer> wordsCount = generateCount(words);
int length = words[0].length();
for (int i = 0; i < length; ++i) {
Map<String, Integer> window = new HashMap<>();
int left = i;
int right = i;
while (right <= s.length() - length && left <= s.length() - length * words.length) {
String sub = s.substring(right, right + length);
incr(window, sub);
if (!wordsCount.containsKey(sub)) {
window.clear();
right += length;
left = right;
continue;
}
while (window.get(sub) > wordsCount.get(sub)) {
decr(window, s.substring(left, left + length));
left += length;
}
right += length;
if (right - left == length * words.length) {
result.add(left);
}
}
}
return result;
}
private Map<String, Integer> generateCount(String[] words) {
Map<String, Integer> wordsCount = new HashMap<>();
for (String word : words) {
incr(wordsCount, word);
}
return wordsCount;
}
private void incr(Map<String, Integer> map, String key) {
if (map.containsKey(key)) {
map.put(key, map.get(key) + 1);
} else {
map.put(key, 1);
}
}
private void decr(Map<String, Integer> map, String key) {
if (map.containsKey(key)) {
Integer value = map.get(key);
if (value <= 1) {
map.remove(key);
} else {
map.put(key, value - 1);
}
}
}
}
