242. 有效的字母异位词
给定两个字符串 s
和t
,编写一个函数来判断t
是否是s
的
字母异位词(字母异位词是通过重新排列不同单词或短语的字母而形成的单词或短语,并使用所有原字母一次。)
。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
提示:
- 1 <= s.length, t.length <= 5 * 10^4
- s 和 t 仅包含小写字母
进阶: 如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
哈希表思路:
数组有时候也可以充当Map
使用
什么时候用哈希表
- 一般来说哈希表都是用来快速判断一个元素是否出现集合里。
什么时候用数组
- 当题目明确指出只有字符串的时候,可以使用数组存储ascii码
当前题目由于都是小写字母,而字母就26
个,故可以建立一个长度是26
的数组,遍历s
和t
,s
中某个字母出现了就将他对应的数组中那个位置加1
,而t相反,是减1
,最后判断数组中每个元素是否为0
即可
因为字符a
到字符z
的ASCII
是26
个连续的数值,所以字符a
映射为下标0
,相应的字符z
映射为下标25
。
在遍历 字符串s
的时候,只需要将s[i] - ‘a’
所在的元素做+1
操作即可,并不需要记住字符a
的ASCII
,只要求出一个相对数值就可以了。 这样就将字符串s
中字符出现的次数,统计出来了。
那看一下如何检查字符串t中是否出现了这些字符,同样在遍历字符串t的时候,对t
中出现的字符映射哈希表索引上的数值再做-1
的操作。
那么最后检查一下,arr
数组如果有的元素不为零0
,说明字符串s
和t
一定是谁多了字符或者谁少了字符,return false
。
最后如果record数组所有元素都为零0,说明字符串s和t是字母异位词,return true。
Go代码
func isAnagram(s string, t string) bool {
/*思路:字母异位词即两个单词中相同字母出现相同次数
由于都是小写字母,而字母就26个,故可以建立一个长度是26的数组
遍历s和t,s中某个字母出现了就将他对应的数组中那个位置加1,
而t相反,是减1,最后判断数组中每个元素是否为0即可*/
if len(s) != len(t) {
return false
}
arr := make([]int,26)
for i := 0;i < len(s);i++{
a := s[i] - 'a'
b := t[i] - 'a'
arr[a]++
arr[b]--
}
for i := 0;i < len(arr);i++ {
if arr[i] != 0 {
return false
}
}
return true
}
49. 字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
示例 2:
输入: strs = [""]
输出: [[""]]
示例 3:
输入: strs = ["a"]
输出: [["a"]]
提示:
- 1 <= strs.length <= 10^4
- 0 <= strs[i].length <= 100
- strs[i] 仅包含小写字母
Go代码
func groupAnagrams(strs []string) [][]string {
//我们可以用一个map[string][]string来完成分组,其中key是单词按字母序排序后的形式,
//value则是所有按字母序排序后是key的单词的集合,由此即完成了分组的任务。
if len(strs) == 0 {
return [][]string{}
}
m := make(map[string][]string)
for i := 0;i < len(strs);i++ {
// sort只提供了对切片的排序,所以先转为字节切片,后面再转回字符串
temp := []byte(strs[i])
sort.Slice(temp,func(i,j int)bool {return temp[i] < temp[j]})
key:= string(temp)
if _,ok := m[key];ok {
m[key] = append(m[key],strs[i])
} else {
arr := make([]string,0)
arr = append(arr,strs[i])
m[key] = arr
}
}
res := make([][]string,0)
for _,val := range m {
res = append(res,val)
}
return res
}
438. 找到字符串中所有字母异位词
给定两个字符串 s
和 p
,找到 s
中所有 p
的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
提示:
- 1 <= s.length, p.length <= 3 * 10^4
- s 和 p 仅包含小写字母
思路:
暴力法
: 异位词排序之后的字符串是相等的,所以可以以s的每一个字符作为起点,取和p相同长度的字符,看看排序后是否相等即可。注:该方法主要是提供了一下思路,在leetcode
上提交的时候会超时。
Go代码
func findAnagrams(s string, p string) []int {
// 异位词排序之后的字符串是相等的
// 所以可以以s的每一个字符作为起点,取和p相同长度的字符,看看排序后是否相等即可
if len(s) == 0 || len(s) < len(p){
return []int{}
}
var res []int
// 注意:因为最后一个字符串长度至少需要len(p)
// 所以遍历的最后一个位置应该是 len(s) - len(p),因此这里是小于 len(s) - len(p) + 1
for i := 0;i < len(s) - len(p) + 1;i++ {
if sortString(s[i:i+len(p)]) == sortString(p) {
res = append(res,i)
}
}
return res
}
// 将字符串s排序后返回新的字符串
func sortString(s string) string {
temp := []byte(s)
sort.Slice(temp,func(i,j int)bool {return temp[i] < temp[j]})
return string(temp)
}
滑动窗口法
:滑动窗口法:异位词的每个字母出现的次数都是一样的,滑动窗口四步走,如下:
- 窗口内容:当前窗口长度与
p
相同 - 如果移动终点:后移直到窗口长度与
p
相同 - 如何移动起点:后移直到窗口长度与
p
相同 - 结果:当前窗口内容是否是
p
的异位词,即每个字符出现次数相同,符合条件则加到结果中。
Go代码
func findAnagrams(s string, p string) []int {
// 滑动窗口法:异位词的每个字母出现的次数都是一样的
/*
1. 窗口内容:当前窗口长度与p相同
2. 如果移动终点:窗口长度与p相同
3. 如何移动起点:窗口长度与p相同
4. 结果:当前窗口内容是否是p的异位词,即每个字符出现次数相同
*/
if len(s) == 0 || len(s) < len(p) {
return []int{}
}
res := make([]int,0)
// p 中每个字符出现的次数
mp := make(map[byte]int)
for i := 0;i < len(p);i++ {
mp[p[i]]++
}
ms := make(map[byte]int) // 记录当前窗口每个字符出现的次数
left,right := 0,0
for ;right < len(s);right++ {
ms[s[right]]++ // 当前字符出现次数加1
if right - left + 1 < len(p) {
continue
}
// 窗口长度和p相等时,比较每个字符出现的次数是否也相等
flag := true // 默认当前窗口和p是异位词
for k,v := range mp {
if cnt,ok := ms[k];!ok || cnt != v {
flag = false
break
}
}
if flag {
res = append(res,left)
}
// 起点后移
ms[s[left]]--
left++
}
return res
}