LeeCode 30
题目:
思路:
朴素的想法,是创建一个 m p mp mp哈希表记录 w o r d s words words中所有字符出现的次数,令 n n n为 s . s i z e ( ) s.size() s.size(), m m m为 w o r d s . s i z e ( ) words.size() words.size(), l e n len len为 w o r d s [ 0 ] . s i z e ( ) words[0].size() words[0].size()。然后枚举 s s s中的每一个字符为一个子串的起点,创建第二个 c n t H A S H cntHASH cntHASH,再遍历子串中的单词,最后检查 c n t cnt cnt是否等于 m p mp mp,显然该算法的时间复杂度为 O ( n × m × l e n ) O(n\times m\times len) O(n×m×len)
根据题目给的数据范围,最坏约为 1 0 9 10^{9} 109,而LeetCode时间限制一秒,一般一秒处理 1 0 7 10^{7} 107左右,所以超时了
Code
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int n = s.length(), m = words.size(), w = words[0].length();
unordered_map<string, int> map;
for (const string& word : words) {
map[word]++;
}
vector<int> ans;
for (int i = 0; i + m * w <= n; i++) {
unordered_map<string, int> cur;
string sub = s.substr(i, m * w);
bool valid = true;
for (int j = 0; j < sub.length(); j += w) {
string item = sub.substr(j, w);
if (map.find(item) == map.end()) {
valid = false;
break;
}
cur[item]++;
}
if (valid && cur == map) {
ans.push_back(i);
}
}
return ans;
}
};
优化:
每个子串的起点是不可避免的需要枚举的,但是我们检查子串是否合法的的过程中,窗口每向前移动一次,我们就要遍历一次子串,与此同时我们发现,当前检查完的 c n t cnt cnt哈希表可以通过删除左侧的单词,添加右侧的的单词,得到窗口向前直接滑一个单词的长度后的 c n t cnt cnt的状态,所以我们把 n m o d l e n n\bmod len nmodlen将起点分为len类(只有 m o d l e n \bmod len modlen才能遍历所有的合法的起点,因为每次要向前滑 l e n len len, i + k × l e n ( 0 ≤ i ≤ l e n − 1 ) i+k\times len (0\leq i\leq len-1) i+k×len(0≤i≤len−1)遍历每个与它相同余数的起点。
AcCode
class Solution
{
public:
vector<int> findSubstring(string s, vector<string> &words)
{
unordered_map<string, int> mp;
for (auto x : words)
{
mp[x]++;
}
vector<int> ans;
int n = s.size();
int m = words.size();
int len = words[0].size();
for (int i = 0; i < len; i++)
{
unordered_map<string, int> cnt;
for (int j = i; j + len <= n; j += len)
{
// j,i为起始位置,len为单词长度
string tmp = s.substr(j, len);
cnt[tmp]++;
// 当有一个完整的窗口时,滑动窗口,每次移动一个单词的长度
if (j >= i + m * len)
{
string tmp = s.substr(j - m * len, len);
// 删除最左边的单词,cnt[tmp]>=1,不会出现负数
cnt[tmp]--;
if (cnt[tmp] == 0)
{
cnt.erase(tmp);
}
}
//j是最后一个单词的第一个字符
if (j >= i + (m - 1) * len)
{
if (cnt == mp)
{
ans.push_back(j - (m - 1) * len);
}
}
}
}
return ans;
}
};