Week 5 @ 2025 算法周记【滑动窗口】
滑动窗口
LC 76. Minimum Window Substring 最小覆盖子串
Minimum Window Substring - LeetCode
func minWindow(s string, t string) string {
expect := toNumArray(t)
window := newNumArray()
result := ""
left, right := 0, 0
for right < len(s) {
window.Add(s[right])
right++
for left < right && window.GreaterThanOrEqual(expect) {
window.Remove(s[left])
result = shorter(result, s[left:right])
left++
}
}
return result
}
type NumArray []int
func newNumArray() NumArray {
return make(NumArray, 52)
}
func (arr NumArray) Add(c byte) {
arr[toNum(c)]++
}
func (arr NumArray) Remove(c byte) {
arr[toNum(c)]--
}
func (arr NumArray) GreaterThanOrEqual(another NumArray) bool {
for i := range arr {
if arr[i] < another[i] {
return false
}
}
return true
}
func toNumArray(s string) NumArray {
arr := newNumArray()
for i := range s {
arr.Add(s[i])
}
return arr
}
func toNum(c byte) int {
if c >= 'a' && c <= 'z' {
return int(c - 'a')
}
if c >= 'A' && c <= 'Z' {
return int(c - 'A') + 26
}
return -1
}
func shorter(base, s string) string {
if base == "" || len(s) < len(base) {
return s
}
return base
}
优化
利用统计「满足窗口条件的字符数」减少 GreaterThanOrEqual 判断的成本
func minWindow(s string, t string) string {
expect := toNumArray(t)
window := newNumArray()
result := ""
valid := 0
expectValid := 0
for _, c := range expect {
if c > 0 {
expectValid++
}
}
left, right := 0, 0
for right < len(s) {
rx := toNum(s[right])
right++
if expect[rx] > 0 {
window[rx]++
if window[rx] == expect[rx] {
valid++
}
}
for left < right && valid == expectValid {
lx := toNum(s[left])
if expect[lx] > 0 {
if window[lx] == expect[lx] {
valid--
}
window[lx]--
}
result = shorter(result, s[left:right])
left++
}
}
return result
}
type NumArray []int
func newNumArray() NumArray {
return make(NumArray, 52)
}
func toNumArray(s string) NumArray {
arr := newNumArray()
for i := range s {
arr[toNum(s[i])]++
}
return arr
}
func toNum(c byte) int {
if c >= 'a' && c <= 'z' {
return int(c - 'a')
}
if c >= 'A' && c <= 'Z' {
return int(c - 'A') + 26
}
return -1
}
func shorter(base, s string) string {
if base == "" || len(s) < len(base) {
return s
}
return base
}
LC 567. Permutation in String 字符串的排列
Permutation in String - LeetCode
func checkInclusion(s1 string, s2 string) bool {
expect := toNumberMap(s1)
window := newNumberMap()
valid := 0
expectValid := 0
for _, c := range expect {
if c > 0 {
expectValid++
}
}
left, right := 0, 0
for right < len(s2) {
rx := toNumber(s2[right])
if expect[rx] > 0 {
window[rx] ++
if expect[rx] == window[rx] {
valid++
}
}
right++
for left < right && valid == expectValid {
lx := toNumber(s2[left])
if expect[lx] > 0 {
if expect[lx] == window[lx] {
valid--
}
window[lx]--
}
if right-left == len(s1) {
return true
}
left++
}
}
return false
}
type NumberMap []int
func newNumberMap() NumberMap {
return make(NumberMap, 26)
}
func toNumberMap(s string) NumberMap {
m := newNumberMap()
for i := range s {
m[toNumber(s[i])]++
}
return m
}
func toNumber(c byte) int {
return int(c - 'a')
}
思路二:定长窗口
func checkInclusion(s1 string, s2 string) bool {
expect := toNumberMap(s1)
window := newNumberMap()
valid := 0
expectValid := 0
for _, c := range expect {
if c > 0 {
expectValid++
}
}
left, right := 0, 0
for right < len(s2) {
rx := toNumber(s2[right])
if expect[rx] > 0 {
window[rx] ++
if expect[rx] == window[rx] {
valid++
}
}
right++
if right - left == len(s1) {
if valid == expectValid {
return true
}
lx := toNumber(s2[left])
if expect[lx] > 0 {
if expect[lx] == window[lx] {
valid--
}
window[lx]--
}
left++
}
}
return false
}
type NumberMap []int
func newNumberMap() NumberMap {
return make(NumberMap, 26)
}
func toNumberMap(s string) NumberMap {
m := newNumberMap()
for i := range s {
m[toNumber(s[i])]++
}
return m
}
func toNumber(c byte) int {
return int(c - 'a')
}
LC 438. Find All Anagrams in a String 找到字符串中所有字母异位词
Find All Anagrams in a String - LeetCode
func findAnagrams(s string, p string) (result []int) {
window := newNumMap()
expect := toNumMap(p)
valid := 0
expectValid := 0
for _, c := range expect {
if c > 0 {
expectValid++
}
}
left, right := 0, 0
for right < len(s) {
if rx := idx(s[right]); expect[rx] > 0 {
window[rx]++
if window[rx] == expect[rx] {
valid++
}
}
right++
if right - left == len(p) {
if valid == expectValid {
result = append(result, left)
}
if lx := idx(s[left]); expect[lx] > 0 {
if window[lx] == expect[lx] {
valid--
}
window[lx]--
}
left++
}
}
return result
}
type NumMap []int
func newNumMap() NumMap {
return make(NumMap, 26)
}
func toNumMap(s string) NumMap {
m := newNumMap()
for i := range s {
m[idx(s[i])]++
}
return m
}
func idx(c byte) int {
return int(c - 'a')
}
LC 3. Longest Substring Without Repeating Characters 无重复字符的最长子串
Longest Substring Without Repeating Characters - LeetCode
func lengthOfLongestSubstring(s string) (result int) {
window := make(map[byte]bool, 66)
dup := false
left, right := 0, 0
for right < len(s) {
rc := s[right]
if !window[rc] {
window[rc] = true
} else {
dup = true
}
right++
if !dup {
result = max(result, right-left)
}
for left < right && dup {
lc := s[left]
if lc == rc {
dup = false
} else {
window[lc] = false
}
left++
}
}
return
}
简化判断逻辑
(时间复杂度一样,但是似乎总执行时间没有上面引入 dup 做判断好 7ms vs 0ms - 当然不确定是否是 leetcode 的原因)
func lengthOfLongestSubstring(s string) (result int) {
window := make(map[byte]int, 66)
left, right := 0, 0
for right < len(s) {
rc := s[right]
window[rc]++
right++
for window[rc] > 1 {
window[s[left]]--
left++
}
result = max(result, right - left)
}
return
}
, multiple selections available,
Related content
Week 43 @ 2024 算法周记【滑动窗口】
Week 43 @ 2024 算法周记【滑动窗口】
More like this
Week 6 @ 2025 算法周记【前缀和 + 滑动窗口】
Week 6 @ 2025 算法周记【前缀和 + 滑动窗口】
More like this
Week 44 @ 2024 算法周记【二分查找】
Week 44 @ 2024 算法周记【二分查找】
More like this
Week 45 @ 2024 算法周记【动态规划+回溯】
Week 45 @ 2024 算法周记【动态规划+回溯】
More like this
Week 51 @ 2024 算法周记【双指针链表+丑数】
Week 51 @ 2024 算法周记【双指针链表+丑数】
More like this
Week 1 @ 2025 算法周记【遍历二维数组 + 双指针数组】
Week 1 @ 2025 算法周记【遍历二维数组 + 双指针数组】
More like this