一、题目描述
给你两个字符串 s1
和 s2
,写一个函数来判断 s2
是否包含 s1
的排列。换句话说,s1
的排列之一是 s2
的 子串 。
二、解题思路
使用滑动窗口,以s1="ab",s2="aidbaooo"
为例,模拟一下滑动窗口的过程。
首先需要计数,获得s1
中每个字符出现的次数,然后遍历s2
,每出现一个s1
中的字符,就将计数减1,当s1
中所有的字符都出现了,并且窗口长度等于s1
的长度时,说明在s2
中找到了s1
的排列。下面是s1
中的字符计数
第一步:遍历s2
,第一个字符是a,就与s1
中的抵消掉,该字符计数减1,a—>0;
第二步:当遍历到第二个字符i
时,发现它不是s1中的字符,将其计数为-1,
第三步:当当前窗口出现不属于s1
中的字符的时候,此时要缩短窗口,left
移动到i
的后面一位,这时我们要重新计数了,因为在新的窗口里每个字符有没有出现还不知道,要恢复原样,新窗口的起始如下,d
出现了,但不属于s1
,所以计数为-1:
第四步:同第三步一样,当当前窗口出现不属于s1
中的字符的时候或者遇到计数为-1时,这是更新窗口,从新的起点开始,计数更新,下一个元素是b
,在s1
中有,则其计数减一,同时其它元素计数根据当前窗口中是否出现更新:
第五步:right
继续后移,a
出现在s1
中,此时整好ab
计数都为0了,并且窗口长度也是s1
的长度,此时返回true
即可。
三、代码演示
class Solution {
public boolean checkInclusion(String s1, String s2) {
int n = s1.length(), m = s2.length();
if (n>m){
return false;
}
//先统计字符串s1中每个字符出现的次数
int[] cnt = new int[26];
for (int i=0; i<n; i++){
cnt[s1.charAt(i)-'a']++;
}
//声明两个指针
int left=0, right=0;
while (right<m){
//拿到s2中当前字符对应在26个字母中的索引
int x = s2.charAt(right)-'a';
cnt[x]--;
while (cnt[x]<0){
//通过缩减窗口使得cnt[x]不为负数,相当于第三步
cnt[s2.charAt(left)-'a']++;
left++;
}
//当前窗口长度等于s1的长度且s1中字符的计数都是0
if (right-left+1==n){
return true;
}
right++;
}
return false;
}
}