LeetCode-567. 字符串的排列
题目来源
题目详情
给你两个字符串 s1
和 s2
,写一个函数来判断 s2
是否包含 s1
的排列。如果是,返回 true
;否则,返回 false
。
换句话说,s1
的排列之一是 s2
的 子串 。
示例 1:
输入: s1 = "ab" s2 = "eidbaooo"
输出: true
解释: s2 包含 s1 的排列之一 ("ba").
示例 2:
输入: s1= "ab" s2 = "eidboaoo"
输出: false
提示:
1 <= s1.length, s2.length <= 104
s1
和s2
仅包含小写字母
相似题目
题解分析
解法一:双指针法
滑动窗口的思想:
用i
,j
表示滑动窗口的左边界和右边界,通过改变i
,j
来扩展和收缩滑动窗口,可以想象成一个窗口在字符串上游走,当这个窗口包含的元素满足条件,即包含字符串T的所有元素,记录下这个滑动窗口的长度j-i+1
,这些长度中的最小值就是要求的结果。
步骤一
不断增加j
使滑动窗口增大,直到窗口包含了T的所有元素
步骤二
不断增加i
使滑动窗口缩小,因为是要求最小字串,所以将不必要的元素排除在外,使长度减小,直到碰到一个必须包含的元素,这个时候不能再扔了,再扔就不满足条件了,记录此时滑动窗口的长度,并保存最小值
步骤三
让i
再增加一个位置,这个时候滑动窗口肯定不满足条件了,那么继续从步骤一开始执行,寻找新的满足条件的滑动窗口,如此反复,直到j
超出了字符串S范围。
如何判断滑动窗口包含了T的所有元素?
- 对于如何判断滑动窗口中包含所有元素,我们可以使用一个整数数组,类似于以前做过的字符串所有相同的字母异位词问题和字母异位词分组问题。
- 这里可以优化的一个地方就是,如果单纯使用cnts数组计数每个字符出现的次数,那么在判断的时候每次都需要判断数组中所有元素的数量都小于等于0时,表示当前滑动窗口不再需要任何元素。
- 具体地,我们可以再使用一个计数指针cnt表示目标串中总共需要计数的字符数,并在滑动窗口的过程中维护该变量。最后,只需要判断cnt是否为0即可。
- 本题与【最小覆盖子串】问题的区别就是,本题中要求子串的排列出现在原串中,而不是子串。排列是有要求的,即字母顺序可以不一样,但是子串必须是连续的。这里可以通过right-left+1是否等于n来判断符合条件的子串是否连续。
class Solution {
public boolean checkInclusion(String s1, String s2) {
int n = s1.length();
int m = s2.length();
int left = 0, right = 0;
int cnt = n;
int[] cnts = new int[26];// 每个字母所需的数量
for(char ch : s1.toCharArray()){
cnts[ch - 'a']++;
}
while(right < m){
char ch = s2.charAt(right);
if(cnts[ch - 'a'] > 0){
cnt--;
}
cnts[ch - 'a']--;
if(cnt == 0){
while(left < right && cnts[s2.charAt(left) - 'a'] < 0){
cnts[s2.charAt(left) - 'a']++;
left++;
}
if(right - left + 1 == n){
return true;
}
}
right++;
}
return false;
}
}
Either Excellent or Rusty