题目链接: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; } };