算法—leetcode—567—字符串的排列

169 阅读2分钟

题目

  1. 字符串的排列

题目描述

给定两个字符串s1和s2,写一个函数来判断s2是否包含s1的排列;
换句话说,第一个字符串的排列之一是第二个字符串的子串。

案例

示例一

输入: s1 = "ab" s2 = "eidbaooo"
输出: True
解释: s2 包含 s1 的排列之一 ("ba").

示例二

输入: s1= "ab" s2 = "eidboaoo"
输出: False

思路

1、我们在字符串S中使用双指针中的左右指针技巧,初始化left = right = 0,把索引左闭右开区间 [left, right) 称为一个「窗口」。
2、我们先不断地增加right指针扩大窗口[left, right),直到窗口中的字符串符合要求(包含了T中的所有字符)。
3、此时,我们停止增加right,转而不断增加left指针缩小窗口[left, right),直到窗口中的字符串不再符合要求(不包含T中的所有字符了);同时,每次增加left,我们都要更新一轮结果。
4、重复第2和第3步,直到right到达字符串S的尽头。

明确问题

1、当移动right扩大窗口时,应该更新那些数据?
2、什么条件下?窗口应该暂停扩大,开始移动left缩小窗口?
3、当移动left缩小窗口时,应该更新那些数据?
4、我们要的结果应该在扩大窗口时还是缩小窗口时进行更新?

回答

1、窗口更新数据&满足条件的个数
2、窗口大小大于len(need)时
3、窗口大小和满足条件的个数
4、缩小窗口是校验如果满足条件的个数正好与要求的数据相等则求得想要的结果

代码

package leetcode

// checkInclusion
// 字符串的排列
func checkInclusion(s1, s2 string) bool {
    // 初始化计数器,分别记录【窗口】中字符的出现次数和T中字符串出现次数
	window := map[byte]int{}
	need := map[byte]int{}
	for i := 0; i < len(s1); i++ {
		cur := s1[i]
		need[cur]++
	}
	// 初始化窗口的两端,区间[left, right)
	left, right := 0, 0

	// 表示窗口中满足need条件的字符个数
	// 如果valid和len(need)的大小相同,则说明窗口已满足条件,已经完全覆盖了字符串t
	valid := 0
    
    // 结束条件:存在满足条件的节点即可
	for right < len(s2) {
        // 即将移入窗口的值
		cur := s2[right]
        right++
        
		// 进行窗口数据更新
        // 首先校验是否在T串
		if _, ok := need[cur]; ok {
			window[cur]++

			// 表示窗口中满足need条件的字符个数
			if window[cur] == need[cur] {
				valid++
			}
		}

		// 判断窗口是否需要收缩:窗口大小大于len(need)时,应为排列嘛,显然长度应该是一样的。
		for (right - left) >= len(s1) {
            // 结束条件:在这里判断是否找到了合法的子串
            if valid == len(need){
                return true
            }
            
			// 将移出窗口的字符
			delS := s2[left]
			left++
			if _, ok := need[delS]; ok {
                // 既移除窗口的值,也要移除验证的值
				if window[delS] == need[delS] {
					valid--
				}
				window[delS]--
			}
		}
	}
    return false
}

参考

来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/pe…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

labuladong的算法小抄