一、题目描述
给定一个字符串 s 和一些长度相同的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。
二、解题思路
方案一:使用哈希查找,具体看代码。
方案二:滑动窗口
以示例一为例,因为words中的单词长度是3,所以每3个字母是一组
第一步:前三个是一组,然后和words中的单词比较,发现bar出现了一次,然后right继续向后找,发现foo也出现了一次,这时words中的每个单词都出现了,且次数也符合,更新当前结果集res=0
。
第二步:right继续向后遍历,这时the并不是words中的单词,就要缩小窗口,left向后移动到the后面重新开始:
重复第一第二步,当满足words中的单词和其次数都在窗口中出现时,则更新res
三、代码演示
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
//声明一个结果集
Map<String, Integer> map = new HashMap<>();
//统计word中每个单词出现的次数
for (String word : words){
//getOrDefault表示当前有word值就获取key对应的value,没有返回默认值0
map.put(word, map.getOrDefault(word, 0)+1);
}
//一个单词的长度,题目说了words中单词长度相同
int oneWordLen = words[0].length();
//单词个数
int wordNum = words.length;
//所有单词总长度
int totalLen = oneWordLen * wordNum;
List<Integer> res = new ArrayList<>();
for (int i=0; i<s.length()-totalLen+1; i++){
//拿到等于所有单词长度之和的字串
String subStr = s.substring(i, i+totalLen);
//统计子串中单词出现的次数
Map<String, Integer> tmpMap = new HashMap<>();
for (int j=0; j<totalLen; j+=oneWordLen){
String oneWord = subStr.substring(j, j+oneWordLen);
tmpMap.put(oneWord, tmpMap.getOrDefault(oneWord, 0)+1);
}
//如果单词出现的次数和原始words中单词出现的此时相同,则符合条件
if (map.equals(tmpMap)){
res.add(i);
}
}
return res;
}
}
时间复杂度:O(n^2)
滑动窗口代码:
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
//声明一个结果集
Map<String, Integer> map = new HashMap<>();
//统计word中每个单词出现的次数
for (String word : words){
//getOrDefault表示当前有word值就获取key对应的value,没有返回默认值0
map.put(word, map.getOrDefault(word, 0)+1);
}
//一个单词的长度,题目说了words中单词长度相同
int oneWordLen = words[0].length();
//单词个数
int wordNum = words.length;
//所有单词总长度
int totalLen = oneWordLen * wordNum;
//声明一个结果集
List<Integer> res = new ArrayList<>();
for (int i=0; i<oneWordLen; i++){
//声明一个指针
int left=i, right=i;
//使用一个变量来存储匹配单词的个数
int matchedWords = 0;
Map<String, Integer> windowMap = new HashMap<>();
while (right<=s.length()-oneWordLen){
//获取到当前单词
String currWord = s.substring(right, right+oneWordLen);
//将当前单词加到窗口里面
windowMap.put(currWord, windowMap.getOrDefault(currWord,0)+1);
matchedWords++;
//如果当前窗口里面单词出现的次数大于words里面单词出现的次数,则缩减窗口
while (windowMap.getOrDefault(currWord,0)>map.getOrDefault(currWord,0)){
String leftWord = s.substring(left,left+oneWordLen);
windowMap.put(leftWord, windowMap.getOrDefault(leftWord,0)-1);
left += oneWordLen;
matchedWords--;
}
if (matchedWords == wordNum){
res.add(left);
}
right += oneWordLen;
}
}
return res;
}
}