【滑动窗口】leetcode 567. 字符串的排列

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 仅包含小写字母

方法一:滑动窗口

解题思路

使用两个数组 cnt1 和 cnt2,cnt1 统计 s1 中各个字符的个数,cnt2 统计当前遍历的子串中各个字符的个数。
由于需要遍历的子串长度均为 n,我们可以使用一个固定长度为n的滑动窗口来维护 cnt2:滑动窗口每向右滑动一次,就多统计一次进入窗口的字符,少统计一次离开窗口的字符。然后,判断 cnt1 是否与 cnt2 相等,若相等则意味着 s1 的排列之一是 s2 的子串。

代码

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        int len1 = s1.size(), len2 = s2.size();
        if(len1 > len2) return false;
        vector<int> cnt1(26), cnt2(26);
        for(int i = 0; i < len1; i++)
        {
            cnt1[s1[i] - 97]++;
            cnt2[s2[i] - 97]++;
        }
        if(cnt1 == cnt2)    return true;
        
        for(int i = len1; i < len2; i++)
        {
            cnt2[s2[i] - 97]++;
            cnt2[s2[i - len1] - 97]--;
            if(cnt1 == cnt2)    return true;
        }
        return false;
    }
};

复杂度分析

  • 时间复杂度:O( n + m + ∣ Σ ∣ n+m+|\Sigma| n+m+∣Σ∣)。
  • 空间复杂度:O( ∣ Σ ∣ |\Sigma| ∣Σ∣)。

方法二:双指针

解题思路

初始时,仅统计 s1 中的字符,则 cnt 的值均不为正,且元素值之和为 -n。
然后用两个指针 left \textit{left} left right \textit{right} right 表示考察的区间 [ left , right ] [\textit{left},\textit{right}] [left,right] right \textit{right} right 每向右移动一次,就统计一次进入区间的字符 x。为保证 cnt \textit{cnt} cnt 的值不为正,若此时 cnt [ x ] > 0 \textit{cnt}[x]>0 cnt[x]>0,则向右移动左指针,减少离开区间的字符的 cnt \textit{cnt} cnt 值直到 cnt [ x ] ≤ 0 \textit{cnt}[x] \le 0 cnt[x]0
注意到 [ left , right ] [\textit{left},\textit{right}] [left,right] 的长度每增加 1, cnt \textit{cnt} cnt 的元素值之和就增加 1。当 [ left , right ] [\textit{left},\textit{right}] [left,right] 的长度恰好为 n 时,就意味着 cnt \textit{cnt} cnt 的元素值之和为 0。由于 cnt \textit{cnt} cnt 的值不为正,元素值之和为 0 就意味着所有元素均为 0,这样我们就找到了一个目标子串。

代码

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        int len1 = s1.size(), len2 = s2.size();
        if(len1 > len2)   return false;
        vector<int> cnt(26);
        for(int i = 0; i < len1; i++)
            cnt[s1[i] - 97]--;
        int left = 0;
        for(int right = 0; right < len2; right++)
        {
            int x = s2[right] - 97;
            cnt[x]++;
            while(cnt[x] > 0)
            {
                cnt[s2[left] - 97]--;
                left++;
            }
            if(right - left + 1 == len1)
                return true;
        }
        return false;
    }
};

复杂度分析

  • 时间复杂度:O( n + m + ∣ Σ ∣ n+m+|\Sigma| n+m+∣Σ∣)。
  • 空间复杂度:O( ∣ Σ ∣ |\Sigma| ∣Σ∣)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值