567. 字符串的排列 ●●

567. 字符串的排列 ●●

描述

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

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

1 < = s 1. l e n g t h , s 2. l e n g t h < = 1 0 4 1 <= s1.length, s2.length <= 10^4 1<=s1.length,s2.length<=104
s1 和 s2 仅包含小写字母

示例

输入:s1 = “ab” s2 = “eidbaooo”
输出:true
解释:s2 包含 s1 的排列之一 (“ba”).

题解

1. 计数 + 暴力

用一个数组记录字母在s1中出现的次数,然后遍历s2,遍历的下标作为匹配字符串的开头,更新记录数组剩余的字母个数,匹配失败则字符串开头往下移动一个,重新遍历匹配,直到n1个字符全部匹配完成。

时间复杂度: O ( n m ) O(nm) O(nm)

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        int n1 = s1.size(), n2 = s2.size();
        if(n1 > n2) return false;
        vector<int> cnt1(26, 0);
        for(char ch : s1){
            ++cnt1[ch-'a'];		// 统计字母在s1中出现的次数
        }
        for(int i = 0; i <= n2 - n1; ++i){
            vector<int> temp(cnt1.begin(), cnt1.end());	// 临时比较数组
            int n = 0;			// 匹配字母数
            for(int j = 0; j < n1; ++j){
                if(temp[s2[i+j] - 'a'] > 0){	// 存在字母可匹配
                    --temp[s2[i+j] - 'a'];		
                    ++n;
                }else{			// s1中已不存在该字母。匹配失败
                    break;
                }
            }
            if(n == n1) return true;	// 所有s1字母匹配完成
        }
        return false;
    }
};
2. 计数 + 双指针

用一个数组统计字母在s1中出现的次数,然后用左右指针来遍历s2,通过数组元素值是否小于零来判断能否匹配,进而控制左右指针的移动。

  • 时间复杂度: O ( n 1 + n 2 + ∣ Σ ∣ ) O(n_1+n_2+|\Sigma|) O(n1+n2+Σ) ∣ Σ ∣ = 26 |\Sigma| = 26 Σ=26
    创建 cnt \textit{cnt} cnt 需要 O ( ∣ Σ ∣ ) O(|\Sigma|) O(Σ) 的时间。
    遍历 s 1 s_1 s1需要 O ( n 1 ) O(n_1) O(n1) 的时间。
    双指针遍历 s 2 s_2 s2 时,由于 left \textit{left} left right \textit{right} right 都只会向右移动,故这一部分需要 O ( n 2 ) O(n_2) O(n2) 的时间。

  • 空间复杂度: O ( ∣ Σ ∣ ) O(|\Sigma|) O(Σ)

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        int n1 = s1.size(), n2 = s2.size();
        if(n1 > n2) return false;
        vector<int> cnt1(26, 0);
        for(char ch : s1){
            ++cnt1[ch-'a'];				// 统计字母在s1中出现的次数
        }
        int left = 0, right = 0;
        while(right < n2){
            int index = s2[right]-'a';	// 右指针上的字符索引
            --cnt1[index];
            while(cnt1[index] < 0){		// s1中没有剩余该字符可匹配
                ++cnt1[s2[left]-'a'];	// 所以左指针右移
                ++left;					// 把左指针释放的字母用于右指针来匹配 cnt1[index] == 0
            }							
            // 如果左指针所有字母都不能匹配,那么将释放到右指针处,此时 left = right + 1, cnt1[index] == 0
            if(right-left+1 == n1) return true;
            ++right;
            // cout << left << "--" << right << endl;
        }
        return false;
    }
};
3. 滑动窗口

用一个数组统计s1中字母的个数,然后通过滑动s1长度的窗口,维护进入和移出的字母的个数来判断是否匹配成功。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值