C++ 串联所有单词的子串
??给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。
?? s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。
- 例如,如果 words = [“ab”,“cd”,“ef”], 那么 “abcdef”, “abefcd”,“cdabef”, “cdefab”,“efabcd”, 和 “efcdab” 都是串联子串。 “acdbef” 不是串联子串,因为他不是任何 words 排列的连接。
??返回所有串联字串在 s 中的开始索引。你可以以 任意顺序 返回答案。
示例1
输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。
子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。
示例2
输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。
示例3
输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接。
子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接。
子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接。
提示:
- 1 <= s.length <= 104
- 1 <= words.length <= 5000
- 1 <= words[i].length <= 30
- words[i] 和 s 由小写英文字母组成
思路/解法
方式一
回溯算法 + KMP字符串匹配算法即可求解,但是时间复杂度过高。
回溯算法可参考:https://blog.csdn.net/qq135595696/article/details/124134925
KMP字符串匹配算法可参考:https://blog.csdn.net/qq135595696/article/details/123439626
class Solution
{
public:
void PrefixTable(string pattern, vector<int>& prefix)
{
prefix[0] = 0;
int len = 0;
int i = 1;
while(i < pattern.length())
{
if(pattern[i] == pattern[len])
{
len++;
prefix[i] = len;
i++;
}
else
{
if(len > 0)
{
len = prefix[len - 1];
}
else
{
prefix[i] = 0;
i++;
}
}
}
}
void MovePrefixTable(vector<int>& prefix)
{
int i;
for(i = prefix.size() - 1; i > 0;i --)
{
prefix[i] = prefix[i - 1];
}
prefix[i] = -1;
}
void KMPSearch(string haystack, string needle, vector<int>& res)
{
int i = 0;
int j = 0;
vector<int> prefix(needle.length(), 0);
PrefixTable(needle, prefix);
MovePrefixTable(prefix);
while(i < haystack.length())
{
if(j == needle.length() - 1 && haystack[i] == needle[j])
{
res.push_back(i - j);
j = prefix[j];
if (-1 == j)
{
j++;
i++;
}
continue;
}
if(haystack[i] == needle[j])
{
i++;
j++;
}
else
{
j = prefix[j];
if(-1 == j)
{
j++;
i++;
}
}
}
}
void TraceBack(vector<string>& words, string back, int len, vector<int>& res, vector<int>& visited, string haystack)
{
if (back.length() == len)
{
KMPSearch(haystack, back, res);
return;
}
for (int i = 0; i < words.size(); i++)
{
if (visited[i] == 1 || (i > 0 && words[i] == words[i - 1] && visited[i - 1] == 0))
{
continue;
}
visited[i] = 1;
back += words[i];
TraceBack(words, back, len, res, visited, haystack);
visited[i] = 0;
back = back.substr(0, back.length() - words[i].length());
}
}
vector<int> findSubstring(string s, vector<string>& words)
{
vector<int> visited(words.size(), 0);
string back;
vector<int> res;
std::sort(words.begin(), words.end());
TraceBack(words, back, words.size() * words[0].length(), res, visited, s);
return res;
}
};
方式二
滑动窗口算法,滑动窗口移动的步长为stride = words[0].size(),窗口的长度为limit = words.size() * stride。
以下用例需要注意:
"lingmindraboofooowingdingbarrwingmonkeypoundcake"
["fooo","barr","wing","ding","wing"]
答案是:[13],仔细观察会发现第四个单词为 ofoo 而不是 fooo,导致错过了答案。
即需要设定滑动窗口的起始位置并不一定是从零开始,而是在一定范围内,为[0,stride - 1]。
作者:dodo_1202 链接:https://leetcode.cn/problems/substring-with-concatenation-of-all-words/solution/by-dodo_1202-cqbe/
class Solution
{
public:
vector<int> findSubstring(string s, vector<string>& words)
{
int n = s.length();
int stride = words[0].length();
int limit = words.size() * stride;
unordered_map<string, int> need;
for(auto w : words)
{
need[w]++;
}
vector<int> res;
for(int start = 0; start < stride; start++)
{
int left = start;
int right = start;
int cnt = 0;
unordered_map<string, int> window;
while(right < n)
{
string curRight = s.substr(right, stride);
if(need.count(curRight))
{
window[curRight]++;
if(window[curRight] == need[curRight])
{
cnt++;
}
}
if(right - left + stride > limit)
{
string curLeft = s.substr(left, stride);
if(need.count(curLeft))
{
if(window[curLeft] == need[curLeft])
{
cnt--;
}
window[curLeft]--;
}
left += stride;
}
if(right - left + stride == limit && cnt == need.size())
{
res.push_back(left);
}
right += stride;
}
}
return res;
}
};
|