题目

给你两个字符串 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 <= 10^4
s1 和 s2 仅包含小写字母

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/permutation-in-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

看提交记录,发现前前后后复习了很多次,也算是这次可以比较快地写出比较优雅的写法。

因为s2中的子串和s1一定等长,所以滑窗大小是固定的,这其实简化了题目的难度(升级一下就是76题)。

因此,比较直接的思路是使用和s1等长的窗口,统计窗口内字符个数和s1是否相同。比较优雅的写法是只使用一个数组,一开始遍历s1的字符将对应位置数值减1,遍历s2时再将对应数值加1,如果cnt的元素为0,就说明该字符在两个字符串中的数量相同。同时,使用一个变量diff维护数组中0的个数,当diff为0时,说明两个字符串的字符种类及个数完全一致。

代码

滑窗

  1. class Solution {
  2. public boolean checkInclusion(String s1, String s2) {
  3. int[] cnt = new int[26];
  4. int n = s1.length();
  5. int m = s2.length();
  6. // 这里特判是必须的,不然下面循环会越界
  7. if (m < n) {
  8. return false;
  9. }
  10. for (int i = 0; i < n; i++) {
  11. // 对于s1的字符,将cnt对应位置--
  12. cnt[s1.charAt(i) - 'a']--;
  13. // 对于s2的字符,将cnt对应位置++
  14. cnt[s2.charAt(i) - 'a']++;
  15. }
  16. // 两个串中个数不同的字符数
  17. int diff = 0;
  18. for (int c : cnt) {
  19. if (c != 0) {
  20. diff++;
  21. }
  22. }
  23. // 这句也是必须的,有可能一开始的滑窗就匹配了
  24. if (diff == 0) {
  25. return true;
  26. }
  27. for (int i = n; i < m; i++) {
  28. int x = s2.charAt(i) - 'a';
  29. int y = s2.charAt(i - n) - 'a';
  30. if (cnt[x] == 0) {
  31. diff++;
  32. }
  33. cnt[x]++;
  34. if (cnt[x] == 0) {
  35. diff--;
  36. }
  37. if (cnt[y] == 0) {
  38. diff++;
  39. }
  40. cnt[y]--;
  41. if (cnt[y] == 0) {
  42. diff--;
  43. }
  44. if (diff == 0) {
  45. return true;
  46. }
  47. }
  48. return false;
  49. }
  50. }