给定一个字符串 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. 最长无重复字符的子串
public class Solution {
/**
* @param s: a string
* @param p: a string
* @return: a list of index
*/
public List<Integer> findAnagrams(String s, String p) {
// write your code here
List<Integer> list = new ArrayList<>();
int[] times = new int[26]; //存储p中每个字母出现的次数
for(char c : p.toCharArray())
times[c-'a']++;
int l=0, r=-1; //s[l...r]为滑动窗口
while(l < s.length()){
if(r-l+1 == p.length())
list.add(l);
if(r+1 < s.length() && times[s.charAt(r+1)-'a'] > 0){
r++;
times[s.charAt(r)-'a']--;
}else{
times[s.charAt(l)-'a']++;
l++;
}
}
return list;
}
}