作者推荐
【二叉树】【单调双向队列】LeetCode239:滑动窗口最大值
本文涉及的基础知识点
题目
给定一个字符串 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 由小写英文字母组成
滑动窗口
时间复杂度: O(nlen) n是s的长度,len是words[i].length。iWindowWidth = len * words.size();
两层循环:第一层时间复杂度O(len),第二层:O(n/len),所以两层循环的时间复杂度是O(n)。
如何判断s[i,i+iWindowWidth) 是 串联子串
将words依次放到一个mHas 中,依次mStrToCount[sSub]–,如果value为0,则移除key。
sSub为s[i,i+len) s[i+len,i+2len) s[i+2len,i+3*len) …
如果mSub为空,说明是串联子串。
s[i,i+iWindowWidth)和s[i+len,i+len+iWindowWidth)
前者多:s[i,i+len)
后者多:s[i+iWindowWidth,i+len+iWindowWidth)
这是滑动窗口的基础。mSub加上前者,减去后者。
代码
核心代码
template<class KEY>
class CKeyCount
{
public:
void Add(const KEY& key, int iCount)
{
Cnt[key] += iCount;
if (0 == Cnt[key])
{
Cnt.erase(key);
}
}
std::unordered_map<KEY, int> Cnt;
};
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
const int len = words.front().length();
const int iWindowWidth = len * words.size();
CKeyCount<string> mStrToCount;
for (const auto& w : words)
{
mStrToCount.Add(w, 1);
}
vector<int> vRet;
for (int i = 0; (i < len)&&(i + iWindowWidth <= s.length()); i++)
{
int j = i;
auto mHas = mStrToCount;
for (; j < i + iWindowWidth; j += len )
{
mHas.Add(s.substr(j, len), -1);
}
if (mHas.Cnt.empty())
{
vRet.emplace_back(j-iWindowWidth);
}
for (; j + len <= s.length(); j += len)
{
mHas.Add(s.substr(j, len), -1);
mHas.Add(s.substr(j-iWindowWidth, len), 1);
if (mHas.Cnt.empty())
{
vRet.emplace_back(j - iWindowWidth+len);
}
}
}
return vRet;
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
测试用例
template<class T>
void Assert(const T& t1, const T& t2)
{
assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
if (v1.size() != v2.size())
{
assert(false);
return;
}
for (int i = 0; i < v1.size(); i++)
{
Assert(v1[i], v2[i]);
}
}
int main()
{
string s;
vector<string> words;
{
Solution sln;
s = "barfoothefoobarman", words = { "foo", "bar" };
auto res = sln.findSubstring(s, words);
Assert(vector<int>{0, 9}, res);
}
{
Solution sln;
s = "wordgoodgoodgoodbestword", words = { "word", "good", "best", "word" };
auto res = sln.findSubstring(s, words);
Assert(vector<int>{}, res);
}
{
Solution sln;
s = "barfoofoobarthefoobarman", words = { "bar", "foo", "the" };
auto res = sln.findSubstring(s, words);
Assert(vector<int>{6, 9, 12}, res);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
2023年4月
class Solution {
public:
vector findSubstring(string s, vector& words) {
m_r = words.size();
m_c = words[0].size();
if (s.length() < m_rm_c)
{
return vector();
}
vector vRet;
for (int i = 0; i < m_c; i++ )
{
findSubstring(vRet,i,s.c_str() + i, words);
}
return vRet;
}
void findSubstring(vector& vRet,int iBeginIndex,string s, vector& words) {
std::multiset mLess(words.begin(), words.end()), mMore;
int i = 0;
for (; i + 1 < m_r; i++)
{
DelOrAdd(mLess, mMore, s.substr(m_ci, m_c));
}
for (; i < s.length() / m_c; i++)
{
DelOrAdd(mLess, mMore, s.substr(m_ci, m_c));
int iPreIndex = i - m_r;
if (iPreIndex >= 0)
{
DelOrAdd(mMore, mLess, s.substr(m_ciPreIndex, m_c));
}
if (0 == mLess.size())
{
vRet.emplace_back(iBeginIndex + m_c*(iPreIndex + 1));
}
}
}
void DelOrAdd(std::multiset& mDel, std::multiset& mAdd, const string& s)
{
auto it = mDel.find(s);
if (mDel.end() != it)
{
mDel.erase(it);
return;
}
mAdd.emplace(s);
}
int m_r, m_c;
};
扩展阅读
视频课程
有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快
速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
相关下载
想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653
我想对大家说的话 |
---|
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用C++ 实现。
评论记录:
回复评论: