一、题目
给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:
s = "barfoothefoobarman",
words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoor" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:
输入:
s = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
输出:[]
二、解答
2.1方法一:map+滑动窗口
public List findSubstring(String s, String[] words) {
List result = new ArrayList();
if (s.length() == 0 || words.length == 0 || words[0].equals("")){
return result;
}
int sLen = s.length();
int wLen = words[0].length();
int len = words.length;
//记录字串与个数
Map wordMap = new HashMap();
for(String w : words){
if (wordMap.containsKey(w)){
wordMap.put(w,wordMap.get(w) + 1);
continue;
}
wordMap.put(w,1);
}
//滑动窗口匹配
for (int i = 0; i < wLen; i++){
//滑动窗口map
Map sMap = new HashMap();
//滑动窗口的开始位置
int first = i;
//当前字符串的起始位置
int start = i;
//当前字符串的结束位置
int end = start + wLen;
//滑动窗口内的字符串个数
int cnt = 0;
while (end <= sLen){
//滑动窗口map的字符串个数超过words数组个数时候,清除第一个字符串
if (cnt > 0 && cnt >= len){
String key = s.substring(first,first + wLen);
if (sMap.containsKey(key)){
if (sMap.get(key) == 1){
sMap.remove(key);
}else {
sMap.put(key,sMap.get(key) - 1);
}
}
first += wLen;
}
String key = s.substring(start,end);
//滑动窗口map添加记录
if (wordMap.containsKey(key)){
if (sMap.containsKey(key)){
sMap.put(key,sMap.get(key) + 1);
}else {
sMap.put(key,1);
}
}else {
//发现存在不在words数组的元素,清空滑动窗口map
sMap.clear();
//滑动窗口从后面开始计算
first = start + wLen;
//滑动窗口cnt归0,但是本循环后面+1了
cnt = -1;
}
//对比窗口与目标words数组是否相等
if (isEquals(wordMap,sMap)){
result.add(first);
}
start += wLen;
end = start + wLen;
cnt++;
}
}
return result;
}
public boolean isEquals(Map l,Map r){
if (l == null || l.size() == 0 || r == null || r.size() == 0 || r.size() != l.size()){
return false;
}
for (Map.Entry map : l.entrySet()){
if (map.getValue() != r.get(map.getKey())){
return false;
}
}
return true;
}