30. Substring with Concatenation of All Words
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 = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
Output: []
方法1: two pointers + hash
Cspiration: https://www.youtube.com/watch?v=L6NLra-rZoU
思路:
注意这道题里的几个restriction:
- 每个word的长度都一样
- word在一个instance当中不能使用超过words中的次数
- 词和词之间不能有间隔
- 顺序任意
用双指针的方法,就是如下的思路:左指针遍历每个位置,开始以右指针j为起点按照m = word.size()为单位检查substring是不是全部在words当中,而且次数吻合。如果有任意不吻合的情况,退出。只有当退出时发现k == 0, 说明n个单词都已经出现过了,才会将左指针i push到结果内。
易错点
- 虽然不明白为什么,但是(int)s.size这个typecase不做会出错
Complexity
Time complexity: O(l^2), l = s.size()
Space complexity: O(n * m)
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if (s.empty() || words.empty()) return {};
vector<int> result;
unordered_map<string, int> hash;
for (auto w: words) hash[w]++;
int n = words.size();
int m = words[0].size();
for (int i = 0; i <= (int)s.size() - m * n; i ++) {
unordered_map<string, int> copy = hash;
int k = n;
int j = i;
while (k > 0) {
string cur = s.substr(j, m);
if (copy[cur] <= 0) break;
copy[cur]--;
k--;
j += m;
}
if (k == 0) result.push_back(i);
}
return result;
}
};
方法2: sliding window
grandyang: https://www.cnblogs.com/grandyang/p/4521224.html
Code_Ganker: https://blog.youkuaiyun.com/linhuanmars/article/details/20342851?utm_source
思路:
Complexity
Time complexity: O(L), L = s.size()
Space complexity: O(m * n)
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if (s.empty() || words.empty()) return {};
vector<int> result;
unordered_map<string, int> hash;
for (auto w: words) hash[w]++;
int n = words.size();
int m = words[0].size();
for (int i = 0; i < m; i++) {
unordered_map<string, int> curMap;
int count = 0;
int left = i;
for (int j = i; j <= (int)s.size() - m; j += m) {
string cur = s.substr(j , m);
if (hash.count(cur)) {
curMap[cur]++;
if (curMap[cur] <= hash[cur]) {
count++;
} else {
while (curMap[cur] > hash[cur]) {
string t1 = s.substr(left, m);
curMap[t1]--;
if (curMap[t1] < hash[t1]) count --;
left += m;
}
}
if (count == n) {
result.push_back(left);
curMap[s.substr(left, m)]--;
count --;
left += m;
}
} else {
curMap.clear();
count = 0;
left = j + m;
}
}
}
return result;
}
};