567. 字符串的排列
题意: 给定两个小写 字母字符串s1和s2,判断s2中是否存在一个连续 子串,是s1的某种排列(排列:字符顺序可不同,但出现次数必须完全相同)
**思路:**固定长度的滑动窗口+频次数组(对每个窗口做一次频次对比)
- 窗口长度等于s1的长度
- 用两个长度26的数组,record[26]数组统计s1中每个字符出现的次数,win[26]数组统计当前窗口内字符的出现次数
- 数组下标0-25对应字母a-z
- 只要某一时刻,win数组==record数组,就返回True
python
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
record = [0] * 26 # s1的字符频次
for i in s1:
pos = ord(i) - ord('a')
record[pos] += 1
left = 0
win = [0] * 26 # 当前窗口的字符频次
for right in range(len(s2)): # 左右指针在s2上滑动窗口
# 右指针每向右一步,就把新字符加入窗口计数
pos = ord(s2[right]) - ord('a')
win[pos] += 1
# 当窗口长度>s1长度时,左指针右移,把移出窗口的字符计数-1
if right - left + 1 > len(s1):
win[ord(s2[left]) - ord('a')] -= 1
left += 1
# 比较两个频次数组
if record == win:
return True
return False
- 时间复杂度:O(n),n为s2的长度
- 虽然每次都要比较两个长度为26的数组,但因为26是常数,所以总体还是O(n)
- 空间复杂度:O(1)
- 只用了两个固定大小的频次数组
424. 替换后的最长重复字符
**题意:**给定只包含大写字母的字符串s和整数k,最多可以替换k个字符为任意大写字母,求替换后能得到的最长相同字符子串长度
**思路:**滑动窗口+哈希表
- 用一个滑动窗口在字符串上从左到右移动
- 用一个哈希表统计窗口中每个字符出现的次数
- 同时维护一个变量maxCount ,表示当前窗口中出现次数最多的那个字符的数量
- 窗口的长度 - maxCount ,就是把当前窗口变成全相同字符所需要替换的字符数
- 如果这个值小于等于k,说明窗口是合法的,可以继续扩展
- 否则说明替换次数不够,需要收缩左边界
**重要优化点:**收缩窗口时,不重新计算maxCount,而是保留历史最大值,即使它偏大,也不会影响最终答案,只是让窗口判断更宽松,从而避免重复计算
python
class Solution:
def characterReplacement(self, s: str, k: int) -> int:
left = 0
win = [0] * 26 # 统计窗口内字符出现的次数
maxCount = 0 # 窗口内出现过的最大字符频次
res = 0
for right in range(len(s)):
# 每加入一个字符就更新字符计数
pos = ord(s[right]) - ord('A')
win[pos] += 1
maxCount = max(win)
win_len = right - left + 1
# 窗口不合法,需要替换的字符数>k,收缩左边界
if win_len - maxCount > k:
win[ord(s[left]) - ord('A')] -= 1
left += 1
res = max(res, right-left+1)
return res
- 时间复杂度:O(n),n为字符串的长度
- 每个字符只会被左右指针各访问一次
- 空间复杂度:O(1)
- 哈希表中最多存26个大写字母
76. 最小覆盖子串
**题意:**两个字符串,一个s,一个t,要求返回s中涵盖t所有字符的最小子串
思路:
-
t_dic字典统计t里的字符及出现次数
-
生成一个字典win_dic,该字典的key和t_dic的key是一样的,value通过后续遍历s字符串的时候再统计
-
定义一个函数isContain,该函数的目的是看win_dic里每个key的值是不是 ≥ t_dic里每个key的值,若不是就直接返回False
-
最后就是滑动窗口的老步骤,right指针在s字符串中逐步往右遍历,若遍历到的字符在字典t_dic里(即是t字符串里的字符),则对应value加1;直到win_dic里每个key的值都 ≥ t_dic里每个key的值;再移动左指针left
python
class Solution:
def minWindow(self, s: str, t: str) -> str:
if len(t) > len(s):
return ""
# key是t中的字符,value是出现次数
t_dic = {}
for i in t:
if i not in t_dic:
t_dic[i] = 1
else:
t_dic[i] += 1
# 窗口dic
win_dic = {}
for k in t_dic:
if k not in win_dic:
win_dic[k] = 0
def isContain(cur_dic, final_dic):
for k in final_dic:
if cur_dic[k] < final_dic[k]:
return False
return True
# 开始遍历s
left, right = 0, 0
win_size = float("inf")
res = ""
for right in range(len(s)):
if s[right] in win_dic:
win_dic[s[right]] += 1
while isContain(win_dic, t_dic):
if right-left+1 < win_size:
win_size = right-left+1
res = s[left: right+1]
if s[left] in win_dic:
win_dic[s[left]] -= 1
left += 1
return res