题目
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明
- 字母异位词指字母相同,但排列不同的字符串。
- 不考虑答案输出的顺序。
示例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" 的字母异位词。
思路
- 字符串s和p,找到s中连续的的几个字符,每个字符的数量等于p。
- 使用两个数组need和window。need负责记录p中每个字符的数量。window用来记录一个位于s上的窗口中,存在的字符是否满足need。
- 使用left和right作为window的左右端点。
- 先扩展window的右边,如果right处的字符数量已经满足了need,则说明当前window中的一个字符数量已经匹配need中该字符的数量,用match表示,match增加一。不停的向右扩展,直到match的数量达到need中的字符类数。
- 再收缩左边界,将不需要考虑的字符排出窗口。如果left处的字符是need中的,那么在window中该字符的数量减一,如果不再满足need,那么match减一,停止收缩,继续向右扩展。如果match数满足,并且window区间的长度等于p,那么久找到了一个字母异位词。
代码
可以使用哈希表,也可以使用数组,但是哈希表比较慢,下面两种都列出来。
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int size = 0; // p中有几种字符
int left = 0, right = 0; // 滑动窗口的左右边界
int need[128] = { 0 };
int window[128] = { 0 };
vector<int> res;
int match = 0;
for ( auto& c : p ) // 计算需求
++need[c];
for ( int i = 0; i < 128; ++i ) // 计算有几种字符
if ( need[i] != 0 )
++size;
while ( right < s.size() ) { // 循环停止条件为右边界到最后
char c1 = s[right]; // 先扩展有边界
if ( need[c1] != 0 ) { // 如果右边界的字符在需求中。
++window[c1];
if ( window[c1] == need[c1] ) // 如果该字符满足需求,match增一
++match;
}
++right; // 右边界增一
while ( match == size ) { // 收缩左边界知道窗口元素不再满足需求
if ( right - left == p.size() ) // 如果窗口中不含有其他字符
res.push_back( left ); // 则左边界满足要求
char c2 = s[left];
if ( need[c2] != 0 ) { // 如果左边界在需求中
--window[c2]; // 窗口减去该元素
if ( window[c2] < need[c2] ) // 少于需求,减少match
--match;
}
++left;
}
}
return res;
}
};
哈希表
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
unordered_map<char, int> need;
unordered_map<char, int> window;
vector<int> res;
int match = 0;
for ( auto& c : p )
++need[c];
int left = 0, right = 0;
while ( right < s.size() ) {
char c1 = s[right];
if ( need.count( c1 ) ) {
++window[c1];
if ( window[c1] == need[c1] )
++match;
}
++right;
while ( match == need.size() ) {
if ( right - left == p.size() )
res.push_back( left );
char c2 = s[left];
if ( need.count( c2 ) ) {
--window[c2];
if ( window[c2] < need[c2] )
--match;
}
++left;
}
}
return res;
}
};