给定一个字符串数组,将所有字符串分组,每一组的字符串包含的字符相同但是顺序不同。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
思路
本文提出了 3 种解法,可以抽象出一个共同的思路:
都是定义了一种映射,将字符串映射到一个key,并保证同一组字符串映射到相同的key,不同组字符串映射到不同的key。
1、排序法
对每个字符串,先排序,然后作为 key,将原字符串加入到 key 对应的 value 中。
这样,不同字符顺序的字符串排序后 key 都相等,因此一个 key 对应的 value 自成一组。
示例:
‘aab’ -> ‘aab’
‘aba’ -> ‘aab’
key 相等,因此分到一组。
排序的算法复杂度为 O(nlogn),因此整体算法复杂度为 O(mnlogn)。
m为字符串个数,n为每个字符串的平均长度。
2、计数法
对每个字符串,对包含的字符的种类和个数进行统计,然后将同样记录结果的字符串聚集在一起。
可以用一个 26 位的字符串作为 key,代表 26 个字母的个数。
示例:
‘aab’ -> ‘21000000000000000000000000’
‘aba’ -> ‘21000000000000000000000000’
key 相等,因此分到一组。
相当于也是用了排序法,只不过是桶排序,算法复杂度为O(n),因此整体算法复杂度为O(mn)。
3、累乘法
将字符串中每个字符对应素数的累乘,将结果作为 key。
26 个字母对应的素数可以是任意的,只要互不相同即可,如:
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
示例:
‘aab’ -> 223=12
‘aba’ -> 232=12
key 相等,因此分到一组。
缺点:乘积结果与字符串长度成指数增长,容易数据溢出。
整体算法复杂度为O(mn)。
python实现
def groupAnagrams(strs):
"""
:type strs: List[str]
:rtype: List[List[str]]
排序法。
"""
dic = dict()
for s in strs:
key = str(sorted(s))
dic[key] = dic.get(key, []) + [s]
return list(dic.values())
def groupAnagrams2(strs):
"""
:type strs: List[str]
:rtype: List[List[str]]
计数法。
"""
dic = dict()
for s in strs:
l = [0] * 26 # 26个字母的计数
for char in s:
l[ord(char) - ord('a')] += 1
key = str(l)
dic[key] = dic.get(key, []) + [s]
return list(dic.values())
from functools import reduce
def groupAnagrams3(strs):
"""
:type strs: List[str]
:rtype: List[List[str]]
累乘法。
"""
prime_list = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
dic = dict()
for s in strs:
if s:
char_to_primes = [prime_list[ord(char) - ord('a')] for char in s]
key = reduce(lambda x,y : x*y, char_to_primes)
else:
key = 0
dic[key] = dic.get(key, []) + [s]
return list(dic.values())
if '__main__' == __name__:
strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
print(groupAnagrams3(strs))