本文正在參加「Python主題月」,詳情查看 活動連接html
這是 LeetCode 上的 面試題 10.02. 變位詞組 ,難度爲 中等。git
Tag : 「哈希表」、「排序」、「計數」、「數學」、「打表」github
編寫一種方法,對字符串數組進行排序,將全部變位詞組合在一塊兒。變位詞是指字母相同,但排列不一樣的字符串。面試
注意:本題相對原題稍做修改數組
示例:markdown
輸入: ["eat", "tea", "tan", "ate", "nat", "bat"],
輸出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
複製代碼
說明:app
一個樸素的想法是根據題意進行模擬,對每一個字符串進行排序做爲 key
,從而實現相同的「變位詞」對應同一個 key
,使用哈希表進行統計便可。oop
Java 代碼:post
class Solution {
public List<List<String>> groupAnagrams(String[] ss) {
List<List<String>> ans = new ArrayList<>();
Map<String, List<String>> map = new HashMap<>();
for (String s : ss) {
char[] cs = s.toCharArray();
Arrays.sort(cs);
String key = String.valueOf(cs);
List<String> list = map.getOrDefault(key, new ArrayList<>());
list.add(s);
map.put(key, list);
}
for (String key : map.keySet()) ans.add(map.get(key));
return ans;
}
}
複製代碼
Python 3 代碼:ui
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
ans = []
hashmap = defaultdict(list)
for s in strs:
key = "".join(sorted(s))
hashmap[key].append(s)
for val in hashmap.values():
ans.append(val)
return ans
複製代碼
方法一沒法作到線性,主要是存在對字符串進行排序的環節。
事實上,咱們能夠利用字符集大小有限做爲切入點(只包含小寫字母),使用一個大小爲
的數組進行計數,而後對計數後的數組統計值進行拼接,做爲哈希表的 key
,從而實現線性複雜度。
Java 代碼:
class Solution {
public List<List<String>> groupAnagrams(String[] ss) {
List<List<String>> ans = new ArrayList<>();
Map<String, List<String>> map = new HashMap<>();
for (String s : ss) {
int[] cnts = new int[26];
for (char c : s.toCharArray()) cnts[c - 'a']++;
StringBuilder sb = new StringBuilder();
for (int i : cnts) sb.append(i + "_");
String key = sb.toString();
List<String> list = map.getOrDefault(key, new ArrayList<>());
list.add(s);
map.put(key, list);
}
for (String key : map.keySet()) ans.add(map.get(key));
return ans;
}
}
複製代碼
Python 3 代碼:
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
ans = []
hashmap = defaultdict(list)
for s in strs:
cnts = [0] * 26
for c in s:
cnts[ord(c) - ord('a')] += 1
key = "_".join(map(str, cnts))
hashmap[key].append(s)
for val in hashmap.values():
ans.append(val)
return ans
複製代碼
事實上,咱們還能使用「質數分解惟一性」性質,使用質數乘積代指某個「變位詞」。
具體的,咱們能夠先使用 static
代碼塊(確保只會發生一次)打表最小的
個質數(任意
個均可以,使用小的,乘積溢出風險低一點),這
個質數分別對應了
個字母。
對於一個「變位詞」而言,其對應的質數乘積必然相同。
Java 代碼:
class Solution {
static int[] nums = new int[26];
static {
for (int i = 2, idx = 0; idx != 26; i++) {
boolean ok = true;
for (int j = 2; j <= i / j; j++) {
if (i % j == 0) {
ok = false;
break;
}
}
if (ok) nums[idx++] = i;
}
}
public List<List<String>> groupAnagrams(String[] ss) {
List<List<String>> ans = new ArrayList<>();
Map<Integer, List<String>> map = new HashMap<>();
for (String s : ss) {
int cur = 1;
for (char c : s.toCharArray()) {
cur *= nums[c - 'a'];
}
List<String> list = map.getOrDefault(cur, new ArrayList<>());
list.add(s);
map.put(cur, list);
}
for (int key : map.keySet()) ans.add(map.get(key));
return ans;
}
}
複製代碼
Python 3 代碼:
class Solution:
nums = [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]
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
ans = []
hashmap = defaultdict(list)
for s in strs:
cur = 1
for c in s:
cur *= self.nums[ord(c) - 97]
hashmap[cur].append(s)
return list(hashmap.values())
複製代碼
使用 long
仍然存在溢出風險,但使用「長度不受限制」的高精度哈希值實現是不現實的。
哈希值必須是有限值域內,纔有意義。
換句話說,若是使用高精度的哈希值的話,咱們是沒法直接將兩個哈希值進行異或判斷結果是否爲 來得出哈希值是否相同的結論,而是須要使用 的複雜度來斷定哈希值是否相同。
所以,針對存在的哈希衝突問題,要麼是解決衝突;要是使用與「字符串哈希」相似的作法,不處理溢出(至關於模 ),但這樣會存在溢出次數不同的值對應的哈希值相同的問題,只能說是一種指望衝突不發生的作法。
這是咱們「刷穿 LeetCode」系列文章的第 No.面試題 10.02
篇,系列開始於 2021/01/01,截止於起始日 LeetCode 上共有 1916 道題目,部分是有鎖題,咱們將先把全部不帶鎖的題目刷完。
在這個系列文章裏面,除了講解解題思路之外,還會盡量給出最爲簡潔的代碼。若是涉及通解還會相應的代碼模板。
爲了方便各位同窗可以電腦上進行調試和提交代碼,我創建了相關的倉庫:github.com/SharingSour… 。
在倉庫地址裏,你能夠看到系列文章的題解連接、系列文章的相應代碼、LeetCode 原題連接和其餘優選題解。