You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters.
Example 1:
Input:
s = "barfoothefoobarman",
words = ["foo","bar"]
Output: [0,9]
Explanation: Substrings starting at index 0 and 9 are "barfoor" and "foobar" respectively.
The output order does not matter, returning [9,0] is fine too.
Example 2:
Input:
s = "wordgoodstudentgoodword",
words = ["word","student"]
Output: []
思路:最初的思路是排列组合出所有模式串的可能,然后根据目标串逐个匹配,但是不能通过所有样例,因为会超时。
所以,借鉴了:https://leetcode.com/problems/substring-with-concatenation-of-all-words/discuss/13656/An-O(N)-solution-with-detailed-explanation的思路:利用窗的思想,固定目标串s,然后滑动匹配,同时利用了所有模式串长度相同的性质,下面是他的代码:
// travel all the words combinations to maintain a window
// there are wl(word len) times travel
// each time, n/wl words, mostly 2 times travel for each word
// one left side of the window, the other right side of the window
// so, time complexity O(wl * 2 * N/wl) = O(2N)
vector<int> findSubstring(string S, vector<string> &L) {
vector<int> ans;
int n = S.size(), cnt = L.size();
if (n <= 0 || cnt <= 0) return ans;
// init word occurence
unordered_map<string, int> dict;
for (int i = 0; i < cnt; ++i) dict[L[i]]++;
// travel all sub string combinations
int wl = L[0].size();
for (int i = 0; i < wl; ++i) {
int left = i, count = 0;
unordered_map<string, int> tdict;
for (int j = i; j <= n - wl; j += wl) {
string str = S.substr(j, wl);
// a valid word, accumulate results
if (dict.count(str)) {
tdict[str]++;
if (tdict[str] <= dict[str])
count++;
else {
// a more word, advance the window left side possiablly
while (tdict[str] > dict[str]) {
string str1 = S.substr(left, wl);
tdict[str1]--;
if (tdict[str1] < dict[str1]) count--;
left += wl;
}
}
// come to a result
if (count == cnt) {
ans.push_back(left);
// advance one word
tdict[S.substr(left, wl)]--;
count--;
left += wl;
}
}
// not a valid word, reset all vars
else {
tdict.clear();
count = 0;
left = j + wl;
}
}
}
return ans;
}
我用java改写了一下,当移除多余词时,一定要注意当前有效词数目nWordNum的条件,这个位置太容易出错了!!!!!!
class Solution {
Map<String,Integer> dic = new HashMap<String,Integer>();
public List<Integer> findSubstring(String s, String[] words) {
if(s == null || s.length() <= 0 || words.length <= 0) return new ArrayList<Integer>();
int allWordNum = 0;
List<Integer> ans = new ArrayList<Integer>();
for(String word : words){
int count = (dic.get(word)==null)?0:dic.get(word);
dic.put(word,count + 1);
allWordNum++;
}
int wordLen = words[0].length();
for(int i = 0 ; i < wordLen ; i++){//起点位置,窗口左界起点
Map<String,Integer> ndic = new HashMap<String,Integer>();
int left = i;//窗口左界
int nWordNum = 0;
for(int j = left + wordLen; j <= s.length(); j += wordLen){//窗口右界
String word = s.substring(j - wordLen,j);
//System.out.println("word : "+word);
if(dic.containsKey(word)){
int c = (ndic.get(word)==null)?0:ndic.get(word);
ndic.put(word,c + 1);
// System.out.println("word count: "+ndic.get(word));
//System.out.println("dic.get(word) : "+word+" : " +dic.get(word));
if(ndic.get(word) <= dic.get(word)){
nWordNum++;
//System.out.println("nWordNum : "+nWordNum);
}else{
while(ndic.get(word) > dic.get(word)){
String delWord = s.substring(left,left + wordLen);
ndic.put(delWord,ndic.get(delWord) - 1);
if(ndic.get(delWord) != dic.get(delWord)){//移除的并非是刚刚添加到当前字典中的多余的词,所以有效数目减少
nWordNum--;
}
left += wordLen;
}
//System.out.println("after while : "+nWordNum);
}
if(nWordNum == allWordNum){
ans.add(left);
//System.out.println("allWordNum : "+allWordNum);
//
// System.out.println("j : "+j);
String visitedWord = s.substring(left,left + wordLen);
ndic.put(visitedWord,ndic.get(visitedWord) - 1);
left += wordLen;
nWordNum--;
//System.out.println("nWordNum : "+nWordNum);
// System.out.println("left : " + left);
// System.out.println(visitedWord);
// System.out.println("nWordNum : "+nWordNum);
// System.out.println(ndic.get(visitedWord));
}
}else{
ndic.clear();
left = j;
nWordNum = 0;
}
}
}
return ans;
}
}