字符串的排列(LeetCode 567)

本文介绍了如何使用滑动窗口和双指针算法解决LeetCode中的字符串排列问题,即判断一个字符串s2是否包含字符串s1的所有排列。通过统计字符个数并动态调整窗口或区间来确定答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.问题描述

给你两个字符串 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 仅包含小写字母

2.难度等级

Medium。

3.热门指数

★★★★☆

出题公司:腾讯、富途。

4.解题思路

4.1 滑动窗口

由于排列不会改变字符串中每个字符的个数,所以只有当两个字符串每个字符的个数均相等时,一个字符串才是另一个字符串的排列。

根据这一性质,统计 s1 的字符个数,然后使用滑动窗口遍历 s2,统计窗口内字符个数是否和 s1 的相等。

如果相等,那么 s2 包含 s1 的排列之一,返回 true。

如果遍历完 s2 仍未找到 s1 的排列之一,返回 false。

注意,因为字符仅包含 26 个小写字母,所以统计字符个数可以使用一个长度为 26 的数组,数组下标与 26 个小写字母一一对应。

时间复杂度: O(m+n),其中 m 为 s1 长度,n 为 s2 长度。

空间复杂度: O(Σ),其中 Σ 是字符集字符数,这道题中的字符集是小写字母,所以 Σ 为 26。

下面以 Go 为例给出实现。

func checkInclusion(s1 string, s2 string) bool {
   m, n := len(s1), len(s2)
    if m > n {
        return false
    }
    var cnt1, cnt2 [26]int
    for i, ch := range s1 {
        cnt1[ch-'a']++
        cnt2[s2[i]-'a']++
    }
    if cnt1 == cnt2 {
        return true
    }
    for i := m; i < n; i++ {
        cnt2[s2[i]-'a']++
        cnt2[s2[i-m]-'a']--
        if cnt1 == cnt2 {
            return true
        }
    }
    return false
}

4.2 双指针

滑动窗口的思路是统计滑动窗口内 s2 的字符数量是否 s1 的相等。

我们也可以反过来,先统计 s1 的字符个数(使用减法),再去考察 s2 是否存在一个区间,其长度恰好为 len(s1),使得其字符个数(使用加法)为零。

在 s2 寻找区间时,可以使用双指针。

第一步:右指针移动,使字符个数加 1。

第二步:
1)如果右指针所指字符个数小于 0 则继续移动右指针。
2)如果字符个数等于 0,则判断双指针构成的区间长度是否为 len(s1),等于则找到,不等于则继续移动右指针。
3)如果字符个数大于 0,则移动左指针,直至字符个数等于零。

时间复杂度: O(m+n),其中 m 为 s1 长度,n 为 s2 长度。

空间复杂度: O(Σ),其中 Σ 是字符集字符数,这道题中的字符集是小写字母,所以 Σ 为 26。

func checkInclusion(s1 string, s2 string) bool {
    m, n := len(s1), len(s2)
    if m > n {
        return false
    }

    var cnt [26]int

    for _, c := range s1 {
        cnt[c-'a']--
    }

    left := 0
    for right, c := range s2 {
        x := c - 'a'
        cnt[x]++
        for cnt[x] > 0 {
            cnt[s2[left]-'a']--
            left++
        }
        if right-left+1 == m {
            return true
        }
    }
    return false
}

参考文献

567.字符串的排列 - LeetCode

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值