题目
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。 换句话说,s1 的排列之一是 s2 的 子串 。 (s1 和 s2 仅包含小写字母
解题思路
关键点
- 一般遇到仅包含字母的题目时就能有特定解法
- 利用ASCII编码 减去'a',即可获得对应的差值(ASCII),差值为0就是a,1就是b(26个字母)
- Arrays.equals: 对比两个数组是否相等
思路分析
- 定义一个26字母的字母表数组来存储个数
- 如果字符串s1 是 s2 的一个排列,只要相同长度内,各个字符的数目一样,就可以排列成功。
- 依据上述规则以s1字符串长度一样的窗口在s2上做移动操作。
- 窗口从左向右移动,每次移动并从s2字符串中移除最右边字符并在数组中减去1,添加左边新加入字符并在数组中加1。
代码
窗口移动
class Solution {
public boolean checkInclusion(String s1, String s2) {
int length1 = s1.getLength();
int length2 = s2.getLength();
if(length1 > length2) return false;
int[] s1Char = new int[26];
int[] s2Char = new int[26];
for(int i = 0; i< length1;i++){
s1Char[s1.charAt(i) - 'a']--;
s2Char[s2.charAt(i) - 'a']--;
}
if(Arrays.equals(s1,s2)) return true;
for(int i = length1; i < length2;i++){
s2Char[s2.charAt(i - length1) - 'a']--;
s2Char[s2.charAt(i) - 'a']++;
if(Arrays.equals(s1,s2)) return true;
}
return false;
// int left = 0;
// for(int r = 0;r < length2;r++){
// int count = s2.charAt(r) - 'a';
// s1Char[count] ++;
// while(s1Char[count] > 0){
// }
// }
}
}
进阶窗口移动
class Solution {
public boolean checkInclusion(String s1, String s2) {
int length1 = s1.length();
int length2 = s2.length();
if(length1 > length2) return false; // 意外情况排除
int[] s1Char = new int[26]; // 记录s1中每个字母数量
for(int i = 0; i< length1;i++){
s1Char[s1.charAt(i) - 'a']--;
}
int left = 0;
for(int r = 0;r < length2;r++){ // 遍历长数组
int count = s2.charAt(r) - 'a';
s1Char[count] ++; // 抵消短数组中的计数值
while(s1Char[count] > 0){ // 如果计数字母大于0 就表明当前s1窗口和s2不吻合
// 不吻合的情况下就要移动窗口 (起始在下标0 右移s1
// 减去最左边一个字母
s1Char[s2.charAt(left) - 'a']--;
left++;
}
if (r - left + 1 == length1){ //若左右指针间长度等于s1的长度
return true;
}
}
return false;
}
}