给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
刚一看题就想到先用 substring 切片,再用 replace 逐一替换判断剩余字符串长度的。后来一想毕竟还是做算法题,不能太赖了,便自行钻研,无果,后借鉴了某邮软院大佬的思想,利用 哈希表(储存词组)+双指针(滑动切片) 进行实现:
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> list = new LinkedList<>();
int wordLength = words[0].length();
int wordsLength = wordLength * words.length;
if (wordsLength > s.length()) {
return list;
}
HashMap<String, Integer> keyMap = new HashMap<>(words.length);
for (String word : words) {
keyMap.put(word, keyMap.getOrDefault(word, 0)+1);
}
int left, right, count;
for (int i = 0; i < wordLength; i ++) {
left = right = i;
count = 0;
// 缓存 记录两指针符合的单词与其数量
HashMap<String, Integer> tempMap = new HashMap<>(words.length);
while (right+wordLength <= s.length()) {
String word = s.substring(right, right+wordLength);
right += wordLength;
if (!keyMap.containsKey(word)) {
tempMap.clear(); // 缓存全清
left = right; // 摒弃(存在非法单词的)两指针间的数据
count = 0;
} else {
tempMap.put(word, tempMap.getOrDefault(word, 0)+1);
count ++;
// 处理形如 s = "ababaab", words = ["ab","ba","ba"] 的情况
// 当重复数据数达上限时我们可以认为在 原left 到 左侧第一个同类型超量数据 间的所有数据都已废弃
while (tempMap.get(word) > keyMap.get(word)) {
String temp = s.substring(left, left+wordLength);
tempMap.put(temp, tempMap.getOrDefault(temp, 0)-1);
left += wordLength;
count--;
}
if (count == words.length) list.add(left);
}
}
}
return list;
}