题目:
给定两个字符串 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
仅包含小写字母
题解:
这题明显可以使用滑动窗口的算法来解决该问题。
首先,准备两个哈希计数数组target和window,分别用于记录字符串p中字符的计数和窗口中字符的计数。target数组记录目标字符串p中每个字符出现的次数,window数组用于记录当前窗口中每个字符出现的次数。
然后,通过不断移动左右指针来构建滑动窗口,找出满足条件的子串,即滑动窗口window中的元素个数和target数组中的个数一致。右指针向右移动,表示扩大窗口;左指针向右移动,表示缩小窗口。
在每次迭代中,右指针指向的字符进入窗口,更新窗口计数数组window,并检查是否需要缩小窗口。如果窗口中某个字符的计数超过了目标字符数组中对应字符的计数,表示该字符不符合要求,左指针向右移动,缩小窗口。
当窗口的大小等于p的长度时,表示找到一个完全匹配的子串,记录下左指针的位置,即为子串的起始索引。
重复上述步骤,直到遍历完整个字符串s,即可找出s中所有p的异位词的起始索引。
该算法的时间复杂度为O(n),因为在每次迭代中,左右指针至多各移动n次。空间复杂度为O(1)。
代码如下:
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> result;
if (s.empty() || p.empty() || s.length() < p.length()) {
return result;
}
vector<int> target(26, 0); // p中各字符的计数数组
vector<int> window(26, 0); // 窗口中各字符的计数数组
// 统计p中每个字符的出现次数
for (char c : p) {
target[c - 'a']++;
}
int left = 0, right = 0;
int count = p.length(); // 计数器,记录窗口中与p异位词相同的字符个数
while (right < s.length()) {
// 右指针指向的字符进入窗口
window[s[right] - 'a']++;
// 如果窗口中某个字符的计数超过了目标字符数组中对应字符的计数,表示该字符不符合要求,左指针向右移动
while (window[s[right] - 'a'] > target[s[right] - 'a']) {
window[s[left] - 'a']--;
left++;
}
// 当窗口大小与p的长度相等,表示找到一个完全匹配的子串,加入结果
if (right - left + 1 == p.length()) {
result.push_back(left);
}
right++; // 右指针右移
}
return result;
}
};