字符串的排列
1.题目描述
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的子串。
2. 示例
示例1:
输入: s1 = "ab" s2 = "eidbaooo"
输出: True
解释: s2 包含 s1 的排列之一 ("ba").
示例2:
输入: s1= "ab" s2 = "eidboaoo"
输出: False
3.读题
这一题的题干和示例清晰明了,题目即说明了,需要判断第一个字符串的排列之一是第二个字符串的子串。
需要注意的和昨天的每日一题相同,也就是子串和子序列的区别,与昨日的子数组和子序列的区别相同,此处不再赘述。
4.思路
首先需要明确的是,需要在第二个字符串里找的子串,它需要是第一个字符串的一个排列。这个条件可以转换为一个更加容易处理的条件,即:该子串和第一个字符串含有字符的种类完全相同,且每个字符的数量也完全相同。
由于所寻找的子串的长度为一个定值,因此考虑用滑动窗口来做,那么就面临着两个问题:
1.窗口的边界如何移动
2.如何比较窗口内字符形成的子串和第一个字符串
关于第一个问题。由于要寻找的子串一定不能包含第一个字符串中不包含的字符,因此一旦窗口内出现了这种字符,左右边界就可以直接向右更新。所以,窗口的边界应该是右边界先动,然后在出现了字符多余(即应该包含该字符但是比第一个字符串数量更多)的情况下移动左边界,出现了不应该包含的字符则同时移动左右边界,其他情况移动右边界。
关于第二个问题,由上述关于第一个问题的分析可以看出,事实上我们只需要对每次循环的左边界和右边界处的字符加入比较即可。因此可以用一个长度为26的数组记录第一个字符串中每个字符的数量,用负数表示数量。
每次有新的字符进出窗口就在数组中更新其数量。如果数组中出现正数,说明出现了两种情况:
1.进入窗口的字符包含在第一个字符串中,但是数量超出了第一个字符串中该字符的数量
2.进入窗口的字符不包含在第一个字符串中。
无论是1或者2哪种情况,都只需要将左边界不断移动直至数组中该值为0,即说明窗口中该字符的数量第一个字符串中相同。
当更新完窗口后窗口的长度等于第一个字符串的长度,窗口中的子串即为所求子串。
5.代码
class Solution {
public boolean checkInclusion(String s1, String s2) {
int[] count = new int[26];
for(char c:s1.toCharArray()){
count[c-'a']--;
}
int left = 0;
char[] cs = s2.toCharArray();
for(int right=0;right<s2.length();right++){
count[cs[right]-'a']++;
while(count[cs[right]-'a']>0){
count[cs[left]-'a']--;
left++;
}
if(right-left+1==s1.length()){
return true;
}
}
return false;
}
}
```