567. 字符串的排列
给你两个字符串
s1
和s2
,写一个函数来判断s2
是否包含s1
的排列。如果是,返回true
;否则,返回false
。
换句话说,s1
的排列之一是s2
的 子串 。
提示:
s1
和s2
仅包含小写字母
示例一:
输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").
示例二:
输入:s1= "ab" s2 = "eidboaoo"
输出:false
解题方法:双指针(滑动窗口)
class Solution {
func getChar(_ s: String, at offset: Int) -> Character {
return s[s.index(s.startIndex, offsetBy: offset)]
}
func checkInclusion(_ s1: String, _ s2: String) -> Bool {
let s1Length = s1.count
let s2Length = s2.count
var s1Freq = [Character: Int]()
var winFreq = [Character: Int]()
var s1Count = 0
var winCount = 0
for i in 0..<s1Length {
let char = getChar(s1, at: i)
s1Freq[char, default: 0] += 1
}
for (_, value) in s1Freq {
s1Count += 1
}
var left = 0
var right = 0
while right < s2Length {
let rightChar = getChar(s2, at: right)
if s1Freq[rightChar] != nil {
winFreq[rightChar, default: 0] += 1
if winFreq[rightChar] == s1Freq[rightChar] {
winCount += 1
}
}
right += 1
while s1Count == winCount {
if right - left == s1Length {
return true
}
let leftChar = getChar(s2, at: left)
if s1Freq[leftChar] != nil {
winFreq[leftChar, default: 0] -= 1
if winFreq[leftChar]! < s1Freq[leftChar]! {
winCount -= 1
}
}
left += 1
}
}
return false
}
}
解题思路
- 排列不讲究顺序,但是字符出现的 种类 和 次数 要恰好对应相等;
- 思考可以使用双指针(一前一后、交替向右边移动)的原因:少考虑很多暴力解法须要考虑的子区间,已经得到了一部分子区间的信息以后,很多子区间都不必要考虑,因此根据特定的情况右指针、左指针交替向右移动,从时间复杂度
O(N^2)
降到O(N)
; - 由于我们只关心子区间里的元素的种数和各个字符出现的次数。因此须要统计字符串
s1
出现的字符的种数和次数,和在字符串s2
上的两个变量所确定的滑动窗口中出现的字符种数和次数; - 还须要设计一个变量
winCount
,表示滑动窗口在s2
上滑动的时候,出现在s1
中的字符的种类数,规则如下:- 如果某一个字符出现的次数恰好等于
s1
中对应字符出现的次数,winCount += 1
; - 在左边界向右移动的过程当中,某一个字符对应的次数减少以后,恰好小于了 s1 对应的字符出现的次数,
winCount -= 1
; - 当滑动窗口中出现的字符种类数和
s1
中出现的字符种类数相等(根据winCount
的定义,对应次数也相等),并且s2
上的滑动窗口的长度等于字符串s1
的长度的时候,就找到了s1
的一个排列。
- 如果某一个字符出现的次数恰好等于
注意:请大家特别留意 winCount
的含义:滑动窗口中的字符要满足字符出现的次数 大于等于 s1
中对应字符出现的次数的时候才加 1
,winCount
不仅统计了种类,还代表了次数。使得我们可以通过 winCount
的数值去了解整个滑动窗口的信息。
复杂度分析
- 时间复杂度:
O(∣s1∣+∣s2∣+∣Σ∣)
; 这里∣s1∣
表示字符串s1
的长度,这里∣s2∣
表示字符串s2
的长度。Σ
表示字符集,即s1
和s2
中出现的所有的字符,∣Σ∣
是字符集的大小,取决于出现字符的最小ASCII
值和最大ASCII
值。- 统计
s1
的频数数组:O(∣s1∣)
; - 计算
s1
中出现的字符种类遍历了频数数组一次:O(∣Σ∣)
; - 双指针遍历了
s2
一次O(∣s2∣)
。
- 统计
- 空间复杂度:
O(∣Σ∣)
。