567. 字符串的排列

118 阅读3分钟

题目:
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 ****的排列。如果是,返回 true ;否则,返回 false 。

换句话说,s1 的排列之一是 s2 的 子串 。

解法:
方法一:滑动窗口+hash 在s2维持长度为len(s1)的滑动窗口,如果窗口内各个字符的出现次数和s1相同,则必然s1的排列能作为s2的子串。用hash来判断字符串个数是否相同

func checkInclusion(s1 string, s2 string) bool {
	s1CharCount := make([]byte, 26)
	s2CharCount := make([]byte, 26)
	for i := range s1 {
		s1CharCount[int(s1[i] - 'a')] ++
	}
	s1Hash := getkey(s1CharCount)
	left, right := 0, 0
	for ; right < len(s2); right ++ {
		s2CharCount[int(s2[right] - 'a')] ++
		if right - left + 1 > len(s1) {
			s2CharCount[int(s2[left] - 'a')] --
			left ++
		}

		if s1Hash == getkey(s2CharCount) {
			return true
		}
	}
	return false
}

func getkey(byt []byte) string {
	return string(byt)
}

方法二:双指针

func checkInclusion(s1 string, s2 string) bool {
    count := make([]int, 26)
    for i := range s1 {
        count[int(s1[i] - 'a')] -- 
    }
    left := 0
    for right := range s2 {
        count[int(s2[right] - 'a')] ++
        // right每进来字符s2[right]之后,保证s2在窗口[left, right]之间,char的数目小于等于s1中char的数目
        for count[int(s2[right] - 'a')] > 0 {
            count[int(s2[left] - 'a')] --
            left ++
        }
        // right - left + 1 = n 则意味着s2在窗口[left, right]之间,已经保持字符串盈亏相等了,盈亏相等该窗口才会增长
        if right - left + 1 == len(s1) {
            return true
        }
    }
    return false
}

方法三 滑动窗口 + 分类讨论
每次滑动窗口,都需要进出一个字符,分类讨论进出的字符是否相等。 用diff记录s2窗口和s1之间不同字符的数量。
用count([26]int)数组记录在窗口内,s2和s1的每个字符差异,s1中每存在一个字符i在count[i]--, s2中每存在一个字符i,count[i]++,count[i] == 0 意味着字符数量达到相等了。
用in代表进入窗口的字符,out代表出窗口的字符。

1.in == out,对窗口内字符数量无影响,窗口整体右移即可
2.in != out,分类讨论:

  • in进入窗口之前如果字符in的数量在s1和窗口已经相等(即count[i] == 0),则in进入窗口之后会打破in字符的平衡,diff++,
  • in进入窗口之前如果字符in的数量在s1和窗口不相等,而in进入窗口之后相等,则diff--。
  • out离开窗口之前如果字符out的数量在s1和窗口已经相等(即count[i] == 0),则out离开窗口之后会打破out字符的平衡,diff++,
  • out离开窗口之前如果字符out的数量在s1和窗口不相等,而out离开窗口之后相等,则diff--。

最后比较diff是否为0即可。

func checkInclusion(s1 string, s2 string) bool {
    if len(s1) > len(s2) {
        return false
    }
    // count的值为0,说明对应的字符抵消了,不为0说明该字符在s1,和s2的窗口中存在差异
    count := make([]int, 26)
    for i := range s1 {
        count[int(s1[i] - 'a')] --
        count[int(s2[i] - 'a')] ++ 
    }
    diff := 0
    for i := range count {
        if count[i] != 0 {
            diff ++
        }
    }
    if diff == 0 {
        return true
    }
    out := 0
    for in := len(s1); in < len(s2); in, out = in + 1, out + 1 {
        // 进窗口和出窗口的字符相等,不影响结果
        if s2[out] == s2[in] {
            continue
        }
        // s2窗口内out字符和s1中的out字符数量相等了,窗口out了一个变成不平衡,diff加一
        if count[int(s2[out] - 'a')] == 0 {
            diff ++
        }
        // s2窗口内out字符和s1中的out字符数量不相相等,窗口out了一个变成平衡,diff减一
        count[int(s2[out] - 'a')] --
        if count[int(s2[out] - 'a')] == 0 {
            diff --
        }

        // s2窗口内in字符和s1中的in字符数量相等,现在in了一个到窗口变成不相等,diff加一
        if count[int(s2[in] - 'a')] == 0 {
            diff ++
        }
        // s2窗口内in字符和s1中的in字符数量不相等,现在in了一个到窗口变成相等,diff减一
        count[int(s2[in] - 'a')] ++
        if count[int(s2[in] - 'a')] == 0 {
            diff --
        }
        if diff == 0 {
            return true
        }
    }
    return false
}