LeetCode 438. Find All Anagrams in a String

20 篇文章 0 订阅
文章介绍了如何解决LeetCode上的第438题,方法是使用滑动窗口算法和哈希映射来检测字符串s中是否存在字符串p的字谜。两种算法思路都涉及到初始化字符计数,然后通过移动窗口并更新计数来找到匹配的字谜。当窗口内字符出现次数与目标字符串一致时,即找到了一个字谜。
摘要由CSDN通过智能技术生成

LeetCode 438. Find All Anagrams in a String

题目描述

Given two strings s and p, return an array of all the start indices of p’s anagrams in s. You may return the answer in any order.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.

思路 1

  1. 使用map记录下字符串p中每个字符出现的次数
  2. 使用滑动窗口算法, 遍历字符串s, 窗口大小固定为p的大小
  3. 窗口每次只向右走一步, 每一轮将窗口右端点处的字符在map中对应的次数-1, 将左端点处的字符在map中的次数+1
  4. 如果窗口中所有字符出现的次数与map中数据一致, 就说明匹配到了一个Anagrams

数据结构 1

var (
	    // 记录p中每个字符出现的次数, 因为只有小写字母, 所以map的大小为26
		fre    = make(map[byte]int, 26)
		// 记录窗口中每个字符出现的次数
		window = make(map[byte]int, 26)
		// 记录匹配到的Anagrams的起始位置
		ans    []int
)

算法 1

func findAnagrams(s string, p string) []int {
    // 如果s的长度小于p的长度, 说明s中不可能存在p的Anagrams
	if len(s) < len(p) {
		return []int{}
	}
	for i := range p {
        // 初始化fre
		fre[p[i]]++
		// 初始化window
		window[s[i]]++
	}
    // 如果初始化后, 窗口中的字符出现的次数与p中的字符出现的次数一致, 说明s的前len(p)个字符就是p的Anagrams
	if equal(fre, window) {
		ans = append(ans, 0)
	}
	// 遍历s, 每次窗口向右滑动一步
	for i := len(p); i < len(s); i++ {
		// 窗口向右滑动, 右端点处的字符在map中对应的次数-1
		window[s[i-len(p)]]--
		// 窗口向右滑动, 左端点处的字符在map中对应的次数+1
		window[s[i]]++

		// 如果窗口满足字谜的要求
		if equal(fre, window) {
			// 把窗口的起始位置加入ans
			ans = append(ans, i-len(p)+1)
		}
	}

	return ans
}

// 判断两个map是否相等
func equal(a, b map[byte]int) bool {
	// 遍历a, a中的字符在b中出现的次数不一致, 说明两个map不相等
	for i, va := range a {
		if va != b[i] {
			return false
		}
	}
	return true
}

思路 2

  1. 使用fre这个map记录下字符串p中每个字符出现的次数, count记录窗口中满足字谜条件的字符个数
  2. 使用滑动窗口算法, 遍历字符串s, 设置两个指针left和right, 开始指向0
  3. right指针向右移动, 每次移动一步, 如果right指向的字符在p中出现过, count++, 表示窗口中满足条件的字符个数+1
  4. right指向的字符在fre中的次数-1, 表示该字符已经被使用过
  5. 如果right-left+1 == p的长度, 说明找到了一个窗口大小为p的长度的窗口
  6. 如果count == p的长度, 说明窗口中的字符串满足字谜的条件, 将left指向的index加入答案
  7. 如果left指向的字符在p中出现过, count–, 表示窗口中满足条件的字符个数-1
  8. left指向的字符在fre中的次数+1, 恢复初始状态, 表示该字符可以再次使用
  9. left指针向右移动一步, left++

数据结构 2

var (
	    // 字符串s的长度
		slen = len(s)
		// 字符串p的长度
		plen = len(p)
	    // 记录p中每个字符出现的次数, 因为只有小写字母, 所以map的大小为26 
		freq = make(map[byte]int, 26)
		// 记录匹配到的Anagrams的起始位置
		ans  = make([]int, 0, slen)
)

算法 2

func findAnagrams(s string, p string) []int {
	if slen < plen {
		return []int{}
	}

	for i := range p {
		freq[p[i]]++
	}

	for left, right, count := 0, 0, 0; right < slen; right++ {
		c := s[right]
		// 判断right指向的字符在p中有没有出现
		if v := freq[c]; v > 0 {
			count++ // 窗口中满足条件的字符个数+1
		}
		freq[c]-- // 出现过则次数-1

		if right-left+1 == plen { // 如果找到了窗口大小为p长度的窗口
			if count == plen { // 如果窗口中的字符串满足字谜的条件
				ans = append(ans, left) // 将left指向的index加入答案
			}
			// 如果left指向的字符在p中出现过
			if v := freq[s[left]]; v >= 0 {
				// 表示窗口中满足条件的字符个数-1
				count--
			}
			// 窗口左边界前进1步
			freq[s[left]]++
			left++
		}
	}

	return ans
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值