题目:
给你两个字符串 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
}