题目难度: 中等
今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复
剑指offer2
就能看到该系列当前连载的所有文章了, 记得关注哦~
题目描述
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的某个变位词。
换句话说,第一个字符串的排列之一是第二个字符串的 子串 。
示例 1:
- 输入: s1 = "ab" s2 = "eidbaooo"
- 输出: True
- 解释: s2 包含 s1 的排列之一 ("ba").
示例 2:
- 输入: s1= "ab" s2 = "eidboaoo"
- 输出: False
提示:
- 1 <= s1.length, s2.length <= 10^4
- s1 和 s2 仅包含小写字母
题目思考
- 如何优化时间复杂度?
解决方案
思路
- 分析题目, 一个很容易想到的方案就是暴力两重循环:
- 外层循环 s2 的每个下标作为起点
- 内层循环从该起点开始, 遍历 s2 的 s1 长度个字符
- 然后记录其每个字符的计数, 并与 s1 字符计数比较
- 相等时则说明 s2 包含 s1 的某个变位词
- 但上述方案复杂度是 O(MN), 根据题目数据规模有可能会超时, 如何优化效率呢?
- 包含问题一般可以采用滑动窗口的思路, 这道题也不例外, 只是需要一些额外的数据结构
- 首先我们可以先比较 s1 和 s2 的长度, 如果 s2 更短, 则一定不可能包含 s1 的变位词, 直接返回 false 即可
- 然后创建两个计数字典 cnts1 (初始化为 s1 的字符计数)和 cnts2 (初始化为空)
- 同时维护一个尚未匹配的字符个数 unmatch, 用于加速判断窗口的有效性, 且其值初始化为 s1 的长度, 因为还没开始遍历 s2
- 接下来开始遍历 s2 的滑动窗口右边界, 并将 cnts2 中对应字符的计数加 1, 然后分两种情况更新 unmatch:
- 如果加 1 之后的 cnts2 计数小于等于 cnts1 计数, 则说明找了一个匹配, unmatch 减 1;
- 否则说明 s2 多了一个该字符, unmatch 要加 1
- 如果当前右边界达到了 s1 的长度, 则需要开始移除上一个左边界, 因为窗口长度是固定的 s1 的长度
- 我们需要将 cnts2 中上个左边界对应字符的计数减 1, 然后同样分两种情况更新 unmatch:
- 如果减 1 之后的计数 cnts2 计数大于等于 cnts1 计数, 则说明移除了一个 s2 的多余字符, unmatch 减 1;
- 否则说明移除后 s1 又多了一个未匹配的该字符, unmatch 要加 1
- 在每次 unmatch 更新完毕后, 我们都检查它是否变为 0, 是的话则说明所有字符都得到了匹配, 返回 true 即可
- 下面代码有详细的注释, 方便大家理解
复杂度
- 时间复杂度
O(N)
: 假设 M 是 s1 的长度, N 是 s2 的长度, 只有 N>=M 时才需要遍历 s2 的 N 个字符 - 空间复杂度
O(M)
: 两个计数字典最多都只有 M 个元素
代码
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
# 滑窗+双计数字典+unmatch变量
if len(s2) < len(s1):
# s2更短, 一定不可能
return False
# 初始化s1计数字典
cnts1 = collections.Counter(s1)
# 初始化s2计数字典为空, 因为尚未添加s2的字符
cnts2 = collections.Counter()
# 初始化未匹配数目是s1的长度
unmatch = len(s1)
le1, le2 = len(s1), len(s2)
for r in range(le2):
# 遍历滑动窗口右边界
rc = s2[r]
cnts2[rc] += 1
if cnts2[rc] <= cnts1[rc]:
# 找到一个匹配
unmatch -= 1
else:
# s2多了一个rc字符
unmatch += 1
if r >= le1:
# 需要移除上个左边界
lc = s2[r - le1]
cnts2[lc] -= 1
if cnts2[lc] >= cnts1[lc]:
# 移除了一个 s2 的多余字符lc
unmatch -= 1
else:
# 移除后s1多了一个未匹配的lc
unmatch += 1
if unmatch == 0:
# 找到一个有效变位词子串
return True
# 遍历完毕后仍未找到有效变位词子串
return False
大家可以在下面这些地方找到我~😊
我的公众号: 算法精选, 欢迎大家扫码关注~😊