You are given a string, S, and a list of words, L, that are all of the same length. Find all starting indices of substring(s) in S that is a concatenation of each word in L exactly once and without any intervening characters.
For example, given:
S: "barfoothefoobarman"
L: ["foo", "bar"]
You should return the indices: [0,9]
.
(order does not matter).
题目的意思是在S中找到包含所有L中出现字符子串的开始下标,不能出现不存在L中,假设L中的单词个数为N,每一个单词的长度为M,对于下标i,
那么有 i 到 i+N*M中的单词刚好包含L中的所有单词一次。
那么就可以这样想了,我们每次就判断一个长度 为 N*M的字符子串 就是
先用两个 map 来保存 总共需要找的单词 totalnumber 和 已经找到的单词 aredyfind 类似 Minimum Window Substring leetcode
1、令 i=0 表示 从 i=0 开始 i最大为 S.size()-N*M ,最大能取
2、然后 依次将 i 到 i+N*M的字符转为单词 ,并且在map中寻找,同时标记找到的
3、如果刚好L中的单词出现了一次,就保存i
否则 跳出来 判断 i+1 到 i+1 + N*M的字符了
代码如下:
class Solution { public: vector<int> findSubstring(string S, vector<string> &L) { vector<int> result; map<string,int> totalnumber; map<string,int> aredyfind; int i,j; int words=L.size(); int wordlength=L[0].size(); for(i=0;i<L.size();i++) ++totalnumber[L[i]]; for(i=0;i<=int(S.size())-words*wordlength;i++) { aredyfind.clear(); for(j=0;j<words;j++) { string str=S.substr(i+j*wordlength,wordlength); if(totalnumber.find(str)==totalnumber.end()) break; ++aredyfind[str]; if(aredyfind[str]>totalnumber[str]) break; } if(j==words){ result.push_back(i); // i=i+words*wordlength; } } return result; } };
我以为 输出 [ 0 ] 其实 0 ,1都是
网上还有一种滑动窗口,上面虽然也算是一种滑动的,但是有些东西需要重新遍历 复杂度为 (S.size()- N*M)*N*M , N为L中单词个数,M为单词长度
其他博客的滑动机制如下:
比如s = “a1b2c3a1d4”L={“a1”,“b2”,“c3”,“d4”}
窗口最开始为空,
a1在L中,加入窗口 【a1】b2c3a1d4
b2在L中,加入窗口 【a1b2】c3a1d4
c3在L中,加入窗口 【a1b2c3】a1d4
a1在L中了,但是前面a1已经算了一次,此时只需要把窗口向右移动一个单词a1【b2c3a1】d4
d4在L中,加入窗口a1【b2c3a1d4】找到了一个匹配
如果把s改为“a1b2c3kka1d4”,那么在第四步中会碰到单词kk,kk不在L中,此时窗口起始位置移动到kk后面a1b2c3kk【a1d4
代码如下:
class Solution { public: vector<int> findSubstring(string S, vector<string> &L) { unordered_map<string, int>wordTimes;//L中单词出现的次数 for(int i = 0; i < L.size(); i++) if(wordTimes.count(L[i]) == 0) wordTimes.insert(make_pair(L[i], 1)); else wordTimes[L[i]]++; int wordLen = L[0].size(); vector<int> res; for(int i = 0; i < wordLen; i++) {//为了不遗漏从s的每一个位置开始的子串,第一层循环为单词的长度 unordered_map<string, int>wordTimes2;//当前窗口中单词出现的次数 int winStart = i, cnt = 0;//winStart为窗口起始位置,cnt为当前窗口中的单词数目 for(int winEnd = i; winEnd <= (int)S.size()-wordLen; winEnd+=wordLen) {//窗口为[winStart,winEnd) string word = S.substr(winEnd, wordLen); if(wordTimes.find(word) != wordTimes.end()) { if(wordTimes2.find(word) == wordTimes2.end()) wordTimes2[word] = 1; else wordTimes2[word]++; if(wordTimes2[word] <= wordTimes[word]) cnt++; else {//当前的单词在L中,但是它已经在窗口中出现了相应的次数,不应该加入窗口 //此时,应该把窗口起始位置想左移动到,该单词第一次出现的位置的下一个单词位置 for(int k = winStart; ; k += wordLen) { string tmpstr = S.substr(k, wordLen); wordTimes2[tmpstr]--; if(tmpstr == word) { winStart = k + wordLen; break; } cnt--; } } if(cnt == L.size()) res.push_back(winStart); } else {//发现不在L中的单词 winStart = winEnd + wordLen; wordTimes2.clear(); cnt = 0; } } } return res; } };
算法时间复杂度为O(S.zize()*M))M是单词的长度