给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入:["eat", "tea", "tan", "ate", "nat", "bat"], 输出: [ ["ate","eat","tea"], ["nat","tan"], ["bat"] ]
说明:
- 所有输入均为小写字母。
- 不考虑答案输出的顺序。
方法一:排序数组分类
思路
当且仅当它们的排序字符串相等时,两个字符串是字母异位词。
算法
维护一个映射
ans : {String -> List}
,其中每个键 K\text{K}K 是一个排序字符串,每个值是初始输入的字符串列表,排序时等于 K\text{K}K。在 Java 中,我们将键存储为字符串,例如,
code
。 在 Python 中,我们将键存储为散列化元组,例如,('c', 'o', 'd', 'e')
。复杂度分析
时间复杂度:O(NKlogK)O(NK \log K)O(NKlogK),其中 NNN 是
strs
的长度,而 KKK 是strs
中字符串的最大长度。当我们遍历每个字符串时,外部循环具有的复杂度为 O(N)O(N)O(N)。然后,我们在 O(KlogK)O(K \log K)O(KlogK) 的时间内对每个字符串排序。空间复杂度:O(NK)O(NK)O(NK),排序存储在
ans
中的全部信息内容。
方法二:按计数分类
思路
当且仅当它们的字符计数(每个字符的出现次数)相同时,两个字符串是字母异位词。
算法
我们可以将每个字符串 s\text{s}s 转换为字符数 count\text{count}count,由26个非负整数组成,表示 a\text{a}a,b\text{b}b,c\text{c}c 的数量等。我们使用这些计数作为哈希映射的基础。
在 Java 中,我们的字符数
count
的散列化表示将是一个用**#**
字符分隔的字符串。 例如,abbccc
将是#1#2#3#0#0#0 ...#0
,其中总共有26个条目。 在 python 中,表示将是一个计数的元组。 例如,abbccc
将是(1,2,3,0,0,...,0)
,其中总共有26个条目。复杂度分析
时间复杂度:O(NK)O(NK)O(NK),其中 NNN 是
strs
的长度,而 KKK 是strs
中字符串的最大长度。计算每个字符串的字符串大小是线性的,我们统计每个字符串。空间复杂度:O(NK)O(NK)O(NK),排序存储在
ans
中的全部信息内容。
public class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<String , List<String>> map = new HashMap<> ();
for(String s : strs) {
char[] chars = s.toCharArray();
Arrays.sort(chars);
String tmp = new String(chars);
if(map.containsKey(tmp)) {
map.get(tmp).add(s);
} else {
ArrayList<String> list = new ArrayList<>();
list.add(s);
map.put(tmp, list);
}
}
List<List<String>> groups = new ArrayList<> ( map.values());
for(int i = 0 ; i < groups.size(); i++) {
List<String> group = groups.get(i);
String[] ss = group.toArray(new String[group.size()]);
Arrays.sort(ss);
groups.set(i, Arrays.asList(ss));
}
return groups;
}
}