给定两个字符串 s 和 p,找到 s 中所有 p 的 变位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
变位词 指字母相同,但排列不同的字符串。
示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的变位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的变位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的变位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的变位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的变位词。
提示:
1 <= s.length, p.length <= 3 * 104
s 和 p 仅包含小写字母
题解
滑动窗口
- left和right标记窗口,左闭右开
[left, right)
区间 - need标记各字符需要的数目,win标记窗口中字符的数目
- valid用于标记窗口中有效的字符数目。仅当窗口中的字符A的数目恰好等于所需要的字符A的数目时,valid + 1
- 当valid等于需要的数目(
need.size
),并且窗口大小(right - left
)和所需要的字符数相等时,将left加入结果集
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res;
map<char, int> need, win;
for (auto ch : p) {
++need[ch];
}
int left = 0;
int right = 0;
int valid = 0;
while (right < s.size()) {
// 需要增加窗口
char ch = s[right];
++right;
if (need.count(ch) != 0) {
// 用"need[ch] != 0"会使得need的size+1
++win[ch];
if (win[ch] == need[ch]) {
++valid;
}
} else {
left = right;
valid = 0;
win.clear();
}
// 需要收缩窗口
while (valid == need.size()) {
if (right - left == p.size()) {
res.push_back(left);
}
ch = s[left];
++left;
--win[ch];
if (win[ch] < need[ch]) {
// 不要用"!=",也不要用"win[ch] == 0"
--valid;
}
}
}
return res;
}
};