给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:
s = “barfoothefoobarman”,
words = [“foo”,“bar”]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 “barfoo” 和 “foobar” 。
输出的顺序不重要, [9,0] 也是有效答案。
链接:https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words
利用循环变量 i ,依次后移,判断每个子串是否符合即可。
怎么判断子串是否符合?这也是这个题的难点了,由于子串包含的单词顺序并不需要固定,如果是两个单词 A,B,我们只需要判断子串是否是 AB 或者 BA 即可。如果是三个单词 A,B,C 也还好,只需要判断子串是否是 ABC,或者 ACB,BAC,BCA,CAB,CBA 就可以了,但如果更多单词呢?那就崩溃了。
用两个 HashMap 来解决。首先,我们把所有的单词存到 HashMap 里,key 直接存单词,value 存单词出现的个数(因为给出的单词可能会有重复的,所以可能是 1 或 2 或者其他)。然后扫描子串的单词,如果当前扫描的单词在之前的 HashMap 中,就把该单词存到新的 HashMap 中,并判断新的 HashMap 中该单词的 value 是不是大于之前的 HashMap 该单词的 value ,如果大了,就代表该子串不是我们要找的,接着判断下一个子串就可以了。如果不大于,那么我们接着判断下一个单词的情况。子串扫描结束,如果子串的全部单词都符合,那么该子串就是我们找的其中一个
List<Integer> resultList = new ArrayList<>();
int wordNum = words.length;
if(wordNum == 0){
return resultList;
}
//把单词保存到map中,value进行累加
HashMap<String,Integer> allWordMap = new HashMap<>();
for (String word : words) {
int value = allWordMap.getOrDefault(word, 0);
allWordMap.put(word,value+1);
}
int wordLength = words[0].length();
//遍历所有子串
for (int i = 0; i < s.length() - (wordNum*wordLength)+1; i++) {
Map<String,Integer> wordMap = new HashMap<>();
int num = 0;
while (num < wordNum){
String word = s.substring(i+num * wordLength,i+(num+1)*wordLength);
if(allWordMap.containsKey(word)){
int value = wordMap.getOrDefault(word,0);
wordMap.put(word,value+1);
if(wordMap.get(word) > allWordMap.get(word)){
break;
}
num++;
}else{
break;
}
}
if(num == wordNum){
resultList.add(i);
}
}
return resultList;