给定一个字符串 s
和一个 非空字符串 p
,找到在 s
中所有关于 p
的字谜的起始索引。
字符串仅由小写英文字母组成,字符串 s 和 p 的长度不得大于 40,000。
输出顺序无关紧要。
样例
给出字符串 s = "cbaebabacd"
p = "abc"
返回 [0, 6]
子串起始索引 index = 0 是 "cba",是"abc"的字谜.
子串起始索引 index = 6 是 "bac",是"abc"的字谜.
解题思路:
哈希方式
分析题目,要找的是待查询序列的一个子序列,这个子序列可以是给定目标序列的任意顺序。我们首先要考虑如何满足所谓的任意顺序。这里很容易想到采取一种哈希策略,使包含目标序列的字符拥有相同的哈希值,从而任何顺序的字符序列都可以被快速识别出来。这样的想法是完全正确的,相比去产生所有可能的序列,这种方法无疑拥有更低的时间复杂度。这种方法还是比较好想到,只需要一个字符数组,保存目标序列各个字符出现的次数即可。这样,只要满足对应数组结构的子序列,就是一个符合要求的子序列。
滑动窗口(sliding window)
字符串匹配问题一个常见的解法就是滑动窗口了,我们利用上述得到的哈希数组,来移动滑动窗口,若窗口内的字符串满足该数组,我们就记录窗口左部的下标作为返回值之一。方法类似于Lintcode 384. 最长无重复字符的子串
class Solution {
public:
/**
* @param s: a string
* @param p: a string
* @return: a list of index
*/
vector<int> findAnagrams(string &s, string &p)
{
// write your code here
int i=0 , j=-1;//[i,j]为滑动窗口
int freq[128] = {0};//记录p中每个字符出现的频次
for(char c : p)
freq[c]++;
vector<int> res;
while(i < s.size())
{
if(j+1 < s.size() && freq[s[j+1]] > 0)
freq[s[++j]]--;
else
freq[s[i++]]++;
if(j-i+1 == p.size())
res.push_back(i);
}
return res;
}
};