题目链接:Substring with Concatenation of All Words
这道题容易想到的是暴力算法,不过一开始就被我否定了,觉得应该不能AC,后来看到网上纯暴力的也能AC,好吧QAQ...
相比暴力算法,可以下列优化措施:
(1)L中的字符串可以看作是固定长度的单个“字符”, 这些字符必须是连续出现,且L中的每个字符都要出现一次,不管重复与否。因此我们可以采用移动窗口的思路。左右边界分别记为Wl,Wr,指针Pt移动的单位长度是L中单个字符串的长度。
i:如果指针所指字符属于L,且该字符重复出现的次数不超过L中重复次数,则Wr = Wr+1;
ii:如果指针所指字符属于L,但是该字符出现的次数已经达到上限,假设从Wl开始,遍历第一次出现该字符的位置为pos,遍历过程中,遇到其他字符,则在维护字符出
现次数的map中,相应字符出现次数减一,更改Wl = pos + 1, Wr = Wr + 1;
iii:如果指针所指字符不属于L,则Wl = Pt + 1, Wr = Wl
(2)如上所述,发现每次指针的移动单位是L中单个字符串长度,因此在最开始的Wl位置应该设为:0~~(单位长度-1),这样才不会出现遗漏的情况
下面贴出AC代码,时间复杂度O(S.length() ):
class Solution{
protected:
vector<int> vi;
unordered_map<string, int> msb;
int Slen, Llen, Lsize;
protected:
void Test(string &S, vector<string> &L, int start){
unordered_map<string, int> msi;
int k = 0, l = 0, len = 0;
string str;
for (k = start; k < Slen && start <= Slen - Llen*Lsize; k += Llen){
str = S.substr(k, Llen);
if (msb.find(str) == msb.end()){
start = k + Llen;
len = 0;
msi.clear();
}
else if (msi[str] == msb[str]){
for (l = start; str != S.substr(l, Llen); l += Llen){
--msi[S.substr(l, Llen)];
--len;
}
start = l + Llen;
}
else{
++msi[str];
++len;
}
if (len == Lsize){
vi.push_back(start);
k = start;
start += Llen;
len = 0;
msi.clear();
}
}
}
public:
vector<int> findSubstring(string &S, vector<string> &L){
Slen = S.length(), Llen = L[0].length(), Lsize = L.size();
for (int i = 0; i < Lsize; ++i) ++msb[L[i]];
for (int st = 0; st < Llen; ++st) Test(S, L, st);
return vi;
}
};