题目

给你两个字符串 s1s2 ,写一个函数来判断 s2 是否包含 s1 的 排列。如果是,返回 true ;否则,返回 false

换句话说,s1 的排列之一是 s2子串

示例 1:

1
2
3
输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").

示例 2:

1
2
输入:s1= "ab" s2 = "eidboaoo"
输出:false

提示:

  • 1 <= s1.length, s2.length <= 104
  • s1s2 仅包含小写字母

实现代码

【力扣76】最小覆盖字串思路一样,使用双指针的滑动窗口来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Solution {
public boolean checkInclusion(String s1, String s2) {

int s1Len = s1.length(),s2Len = s2.length();
if (s1Len > s2Len) {
return false;
}
// 维护字符出现的次数
int[] windowCount = new int[26];
int[] subStrCount = new int[26];
int left = 0, right = 0, n = 0, m = 0;
for (char c : s1.toCharArray()) {
if (subStrCount[c - 'a'] == 0) {
m++;
}
subStrCount[c - 'a']++;
}
while (right < s2Len) {
char c = s2.charAt(right);
right++;
if (subStrCount[c - 'a'] > 0) {
windowCount[c - 'a']++;
if (windowCount[c - 'a'] == subStrCount[c - 'a']) {
n++;
}
}
if ((right - left) == s1Len) {
if (n == m) {
return true;
}
char c1 = s2.charAt(left);
if (subStrCount[c1 - 'a'] > 0) {
if (windowCount[c1 - 'a'] == subStrCount[c1 - 'a']) {
n--;
}
windowCount[c1 - 'a']--;
}
left++;
}
}
return false;
}
}

代码讲解

代码一

1
2
3
// 维护字符出现的次数
int[] windowCount = new int[26];
int[] subStrCount = new int[26];

【力扣76】最小覆盖字串这道题思路不同的是这里通过两个数组维护字符串出现的次数;

为什么?

题目规定s1s2 仅包含小写字母,也就是说最多26个字母,因为每个字母对应的ASCLL码都是有序的,比如aASCLL码97b98c99……,如果将对应的字母减去aASCLL码值,就是数组对应的角下标,所以字符出现的次数就能维护在对应字母的数组角下标位置;

代码二

1
2
3
4
5
6
7
8
9
10
11
12
13
if ((right - left) == s1Len) {
if (n == m) {
return true;
}
char c1 = s2.charAt(left);
if (subStrCount[c1 - 'a'] > 0) {
if (windowCount[c1 - 'a'] == subStrCount[c1 - 'a']) {
n--;
}
windowCount[c1 - 'a']--;
}
left++;
}

这里将while改为了if,因为窗口大小是固定的,执行left++之后循环就会退出来,也就是内部代码只能跑一次就会出来;

时间复杂度

最差的情况是left扫了一遍s2right扫了一遍s2,那就是2*s,加上s1s2所有字符集,最大26,去除常数最终的时间复杂度是O(N)

空间复杂度

额外空间leftright和两个数组,leftright的空间复杂度是O(1)可以不计,因为数组是存放字符次数的,大小取决于s1s2不同字符的个数,最大也就26,所以去除常数的空间复杂度为O(1)