leetcode438 找到字符串中所有字母的异位词

题目

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

示例

输入: s = “cbaebabacd”, p = “abc”
输出: [0,6]
解释:
起始索引等于 0 的子串是 “cba”, 它是 “abc” 的异位词。
起始索引等于 6 的子串是 “bac”, 它是 “abc” 的异位词。

解析

先写最终的方法(具体的注释在最下面):

func findAnagrams(s, p string) (ans []int) {
	sLen := len(s)
	pLen := len(p)
	if sLen < pLen {
		return
	}
	var sWindow, pWindow [128]int
	for i, val := range p {
		sWindow[s[i]]++
		pWindow[val]++
	}
	if sWindow == pWindow { // 首次
		ans = append(ans, 0)
	}

	for i, val := range s[:sLen-pLen] {
		sWindow[val]-- // 左边窗口先减
		sWindow[s[i+pLen]]++

		if sWindow == pWindow {
			ans = append(ans, i+1)
		}
	}
	return
}

这道题还是用滑动窗口来处理(看题解也费了半天劲),如果按照之前的拿到“无重复字符的最长子串”的思路来做的话,大概是下面这个样子:

func findAnagrams(s string, p string) (ans []int) {
	window := [128]int{} // 存储每个字符出现的次数
	for _, ch := range p {
		window[ch]++ // 把P中每个字符都先放到窗口数组中
	}
	left := 0 // 滑动窗口左边界
	for right, ch := range s { // 遍历s字符串
		window[ch]--         // 先将遍历到的字符在窗口中减1
		for window[ch] < 0 { // 表示字符ch的出现次数,已经超过了其在P中的出现次数
			window[s[left]]++ // 窗口右移(指左边界右移)
			left++
		}
		if right-left+1 == len(p) {
			ans = append(ans, left)
		}
	}
	return
}

第二种滑动窗口的方法如下:

func findAnagrams1(s, p string) (ans []int) {
	sLen := len(s)
	pLen := len(p)
	if sLen < pLen {
		return
	}
	var sWindow, pWindow [26]int

	for i, c := range p { // 先把p长度的数据都存进各自的滑动窗口中
		sWindow[s[i]-'a']++
		pWindow[c-'a']++
	}
	if sWindow == pWindow { // 前置位置的判断,只需要这一次判断
		ans = append(ans, 0)
	}

	// 在这之后pWindow只是用来比较的,没有更新的场景了
	for i, c := range s[:sLen-pLen] { // 从s字符串的首位开始遍历(这里减去P长度的意思是,每个窗口的大小是pLen,不减的话最后一位就越界了)
		// 在这里的移动窗口,不是根据left、right来处理的,而是将当前窗口的第一个字符在数组中的位置减1
		sWindow[c-'a']--         // 将窗口内对应字符的值-1
		sWindow[s[i+pLen]-'a']++ // 将最后一个字符的下一个字符加1(相当于窗口整体滑动了一位)

		if sWindow == pWindow {
			ans = append(ans, i+1) // 如果滑动后的新窗口满足条件,将起始下标返回,此时在这个的循环中,因为已经滑动了,起始下标就是i+1
		}
	}
	return
}

整体上来看,第二种方法比较符合认知中的滑动窗口,而第一种方法是和之前的题目解法相似,且只需要新开一个数组就可以。
另外,第二道题如果不想每次-'a’的话,可以把数组的长度设置为128(ASCII的长度)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值