暴力 hash
没有给输入样例长度
对list里面的每个单词统计里面的字母和词频,得到由这些字母组成的字典
如果两个字母对应的字典是相同的,那么它们就是字母异位词
现在的问题就是找到字典下对应的所有字母异位词即可,直接构造不可取,一共有n!种组合,还要比较,直接遍历原来的list即可
但是字典不能在python中hash,我们转换成用下标来hash,用que来存放队列,添加时用in来判断字典是否在里面
虽然这个字母都是小写字母,但是根据我的经验判断,使用库dict比自己手写开数组效率要高
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
que = []
sm = dict()
for i in strs:
t = Counter(i)
sm[i] = t
# 比较n次
if t not in que:
que.append(t)
mp = defaultdict(list)
for i in strs:
t = sm[i]
mp[que.index(t)].append(i)
ans = list(mp.values())
return ans
时间复杂度
O
(
N
2
)
O(N^2)
O(N2),在第一个for中判断是否存在队列也是O(N)级别的开销,第二个for里面,que.index()
也是O(N)级别的算法
sort
开始有想过sort,但是被nlogn的时间复杂度劝退了
不过写了一下,发现效率变高了
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
mp = defaultdict(list)
for i in strs:
mp["".join(sorted(i))].append(i)
return list(mp.values())
用sorted过后的字符串作为key,很容易就能得到答案
时间复杂度这里是 O ( n k l o g k ) O(nklogk) O(nklogk),k是字符串的长度,n是字符串的个数
计数
和暴力一样的思路,不过这里是手动开数组,然后转换成tuple作为key存放到tuple里面,确实简洁了很多
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
mp = defaultdict(list)
for s in strs:
bucket = [0] * 26
for ch in s:
bucket[ord(ch) - ord('a')] += 1
mp[tuple(bucket)].append(s)
return list(mp.values())
现在回过头来看第一次写的暴力,写得太混乱了,转了两次map,只需要一个map就行
prime hash
使用hash的原理来解题,由于字母的范围是小写字母,将它们唯一映射到质数上,然后对于每个字符串,计算它们的乘积,只有异构字符串的乘积会是一样
primes = {'a': 2,
'b': 3,
'c': 5,
'd': 7,
'e': 11,
'f': 13,
'g': 17,
'h': 19,
'i': 23,
'j': 29,
'k': 31,
'l': 37,
'm': 41,
'n': 43,
'o': 47,
'p': 53,
'q': 59,
'r': 61,
's': 67,
't': 71,
'u': 73,
'v': 79,
'w': 83,
'x': 89,
'y': 97,
'z': 101
}
subLists = {}
for string in strs:
product = 1
for character in string:
product = primes[character] * product
if product in subLists.keys():
listA = subLists[product]
listA.append(string)
subLists[product] = listA
else:
subLists[product] = [string]
listToReturn = []
for value in subLists.keys():
listToReturn.append(subLists[value])
return listToReturn
ref: https://leetcode.com/problems/group-anagrams/solution/
改动了一下,几乎是最快的解法
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
primes = {'a': 2,
'b': 3,
'c': 5,
'd': 7,
'e': 11,
'f': 13,
'g': 17,
'h': 19,
'i': 23,
'j': 29,
'k': 31,
'l': 37,
'm': 41,
'n': 43,
'o': 47,
'p': 53,
'q': 59,
'r': 61,
's': 67,
't': 71,
'u': 73,
'v': 79,
'w': 83,
'x': 89,
'y': 97,
'z': 101
}
subLists = {}
for string in strs:
product = 1
for character in string:
product = primes[character] * product
if product in subLists.keys():
subLists[product].append(string)
else:
subLists[product] = [string]
return list(subLists.values())
python里面不用担心溢出的问题
总结
早上思路有些混乱,写出来的暴力很难看
其实思路是想到了,就是统计字典,然后反过来映射这个字典构造出来的字符串,但是实现的方法不是很好,导致效率很低
计数和sort方法都是构造出key,然后存放进dict里面,然后直接取value就行
我的暴力先是统计每个str的dict存放到que队列里面,que中每个dict唯一,然后再重新遍历str,按照key:que中的下标,value:str的格式存放到最终的dict里面
最后一个用质数乘积来作为key的思想很巧妙