题目链接
题目:
分析:
- 要找的是在s中和p是异位词的子串, 也就是说子串大小和p相同, 那么就是窗口大小固定的滑动窗口问题
- 可以使用哈希数组来记录每个元素出现的个数, 定义hash1存放p中的各元素个数
- 定义left = 0; right = 0;
- 进窗口 让right指向的元素进窗口, 即更新hash2中的元素即个数
- 判断 判断窗口大小是否小于等于p的长度
- 出窗口 如果窗口大小大于p的长度, 则出窗口, 此时不需要循环出窗口, 因为窗口大小是固定的, left只需向前移动一步即可
- 更新结果 如果此时hash2和hash1相等 说明此子串和p是异位词, 返回此时left
思考:
想要判断hash1和hash2相等, 需要分别遍历两个数组, 虽然这道题中元素都是小写字母, 时间复杂度不高, 但如果元素变复杂, 则很难比较相同, 如果我们可以用另一种方式:
- 定义一个count = 0, 来记录窗口中"有效数据"的个数, 有效数据是指此数据是p中的元素且此元素在窗口中的个数小于等于p中的个数
- 在进窗口后, 如果此时right指向的元素在hash2中的个数小于在hash1中的个数, 说明此时是有效数据, 则count++
- 在出窗口前,如果此时left指向的元素在hash2中的个数小于在hash1中的个数,说明此时是有效数据, 则count--
- 更新结果时,只需要判断此时count的大小和p的大小是否相等, 如果相等, 则返回left
为什么在进窗口后判断是否更改有效数据的个数?
因为进窗口之后, 才会更新hash2中的值, 更新后再判断此时的值是否小于等于hash1中的值, 此时如果小于等于的话, 才说明这个字符是有效的
为什么在出窗口前判断是否更改有效数据的个数?
因为进窗口之前, 还没有更新hash2中的值, 判断此时的值是否小于等于hash1中的值, 如果此时小于等于的话, 说明这个字符是有效的, 如果出窗口就会失去一个有效字符, 所以要在出窗口前判断是否更改有效数据的个数, 如果在出窗口后判断, hash2中的值已经被更改, 无法知道这个字符是否是有效的
代码:
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> list = new ArrayList<>();
char[] ss = s.toCharArray();//为了方便转化成字符数组
char[] pp = p.toCharArray();
int[] hash1 = new int[26];
for(char x:pp){
hash1[x - 'a']++;
}
int[] hash2 = new int[26];
int left = 0;
int right = 0;
int count = 0;
while(right<ss.length){
char in = ss[right];
hash2[in-'a']++;//进窗口
if(hash2[in-'a'] <= hash1[in-'a']) count++;//进窗口后判断
if(right-left+1>pp.length){
char out = ss[left];
if(hash2[out-'a'] <= hash1[out-'a']) count--;//出窗口前判断
hash2[out-'a']--;//出窗口
left++;
}
if(count == pp.length){
list.add(left);
}
right++;
}
return list;
}
}