Leetcode演算法——49、字串分組(group anagrams)
給定一個字串陣列,將所有字串分組,每一組的字串包含的字元相同但是順序不同。
示例:
輸入: ["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))