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.
For example, given:
s: "barfoothefoobarman"
words: ["foo", "bar"]
You should return the indices: [0,9]
.
(order does not matter).
题意:
给定一个字符串s, 和一些【长度相同的字符串】列表V。设V的大小为|V|, V中字符串的长度为wordLen
则对字符串s的某一个位置j,若j为起始位置,长度为|V| * wordLen 子串,是V中的所有元素的某一种拼接结果,则为满足条件。按顺序输出所有满足条件的j
注意: V中的元素会有重复。
思路:
最暴力的解法就是枚举每一个位置,查看其后子串是否满足条件。复杂度O(|s| * |V| * wordLen) 不优雅。
分析上面的方法我们可以发现,对于枚举j和j+wordLen位置,中间有一大块运算是重复的。
因此我们维护一个窗口,定义aim<string, int>为一个长度为wordLen的子串的期待的个数。(从V中搞定), now<string, int>为目前所需的该子串的数量, 枚举的单位为长度为wordLen的字符串。 发现首先枚举0, wordLen, 2 * wordLen , .. ; 然后枚举1 , 1 + wordLen, 。。。。; 2 , 2 + wordLen ,。。。; 直至wordLen - 1, wordLen - 1 + wordLen, 。。。, 即可枚举完所有的串。且每一块枚举满足每一个元素为连续且不重叠的长度为wordLen的串。
当右端的加入满足条件(即设右端的子串为y,now[y] > 0), 曾将其加入。可以看到该元素若不加入则左边所有的信息都应清零,且存在左边的某些串就是y,删去它可以使得now[y] > 0 条件成立。因此在此条件不满足时,需要逐个从窗口中删去左边的子串。然后判定是否能加入(now[y] > 0)。
判定成功的条件即为窗口中的元素个数 = |V|。
注意事项:
不能在now[y]为0的时候简单判断不能加入,而是要看左边能不能删掉一些使得右边能加入。
若确实不能加入,记得更新left
code:
class Solution { public: vector<int> findSubstring(string s, vector<string>& words) { vector<int> res; if(words.size() == 0 || s == "") return res; unordered_map<string, int> aim; for(int i = 0; i < words.size(); i++){ aim[words[i]] += 1; } int wordLen = words[0].length(); int strLen = s.length(); for(int i = 0; i < wordLen; i++){ int left = i, cnt = 0; unordered_map<string, int> now = aim; for(int j = i; j <= strLen - wordLen; j += wordLen){ string temp = s.substr(j, wordLen); /**!! notion: . case like "barfoofoobarthefoobarman" ["bar","foo","the"] , we can not throw all previous information, but delete some useless information **/ while((now.count(temp) == 0 || (now[temp] == 0)) && left < j){ cnt --; now[s.substr(left, wordLen)] ++; left += wordLen; } if(now.count(temp) == 1 && now[temp] != 0) { now[temp] --; cnt ++; if(cnt == words.size()){ res.push_back(left); } }else{ /** notion : if we do not use this substring, then the left side need to be updated. **/ left = j + wordLen; } } } sort(res.begin(), res.end()); return res; } };