Given an array of strings strs
, group the anagrams together. You can return the answer in any order.
An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.
Example 1:
Input: strs = ["eat","tea","tan","ate","nat","bat"] Output: [["bat"],["nat","tan"],["ate","eat","tea"]]
Example 2:
Input: strs = [""] Output: [[""]]
Example 3:
Input: strs = ["a"] Output: [["a"]]
Constraints:
1 <= strs.length <= 104
0 <= strs[i].length <= 100
strs[i]
consists of lower-case English letters.
字母异位词分组。
给定一个字符串数组,将字母异位词组合在一起。可以按任意顺序返回结果列表。字母异位词指字母相同,但排列不同的字符串。
做这个题之前,需要做如下几个题,对anagram的概念有所了解。
387. First Unique Character in a String
此题比较粗暴的解法是把 input 里面所有的单词都按照字母排序,形成一个新的单词。比如"eat"排序后会成为"aet",然后以"aet"为key,"eat"为 value 加入 hashmap。遍历完 input 之后,输出 hashmap 所有的 value 即可。
时间O(nlogn) - sort
空间O(n) - hashmap
JavaScript实现
1 /** 2 * @param {string[]} strs 3 * @return {string[][]} 4 */ 5 var groupAnagrams = function (strs) { 6 const map = {}; 7 for (let str of strs) { 8 const key = [...str].sort().join(''); 9 if (!map[key]) { 10 map[key] = []; 11 } 12 map[key].push(str); 13 } 14 return Object.values(map); 15 };
Java实现
1 class Solution { 2 public List<List<String>> groupAnagrams(String[] strs) { 3 // corner case 4 if (strs == null || strs.length == 0) { 5 return new ArrayList<List<String>>(); 6 } 7 8 // normal case 9 HashMap<String, List<String>> map = new HashMap<>(); 10 for (String str : strs) { 11 char[] ca = str.toCharArray(); 12 Arrays.sort(ca); 13 String keyStr = String.valueOf(ca); 14 if (!map.containsKey(keyStr)) { 15 map.put(keyStr, new ArrayList<String>()); 16 } 17 map.get(keyStr).add(str); 18 } 19 return new ArrayList<List<String>>(map.values()); 20 } 21 }
但是如上不是最优解,最优解还是要通过counting sort的思路来做。对于每个单词,需要创建一个长度为26的数组(题目说了只有小写)来存这个单词里面每个字母都分别出现过几次。这样每个单词最后形成的数组会长类似这样,
[ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ][ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ][ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ]
然后把这个数组稍微变化一下,做成一个类似这样的string
1#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#0#0#0#0#0#0
把这个string当做hashmap的key。这样每个扫描过的单词都会变成这样,是错位词的单词们的string会长得一样,所以在hashmap中会被存到同一个key里面。最后以数组形式输出hashmap里面所有的value即可。或者中间不加#也能做,只需要把charArray convert成string即可,把这个string当做key存入hashmap。
时间O(n) - 没有排序
空间O(n)
JavaScript实现
1 /** 2 * @param {string[]} strs 3 * @return {string[][]} 4 */ 5 var groupAnagrams = function (strs) { 6 const map = new Map(); 7 for (const s of strs) { 8 const letters = Array(26).fill(0); 9 for (const c of s) { 10 letters[c.charCodeAt(0) - 97]++; 11 } 12 console.log(letters); 13 const key = letters.join("#"); 14 console.log(key); 15 let val = []; 16 if (map.has(key)) { 17 val = map.get(key); 18 } 19 val.push(s); 20 map.set(key, val); 21 } 22 23 return Array.from(map.values()); 24 };
Java实现
1 class Solution { 2 public List<List<String>> groupAnagrams(String[] strs) { 3 // corner case 4 if (strs == null || strs.length == 0) { 5 return new ArrayList<>(); 6 } 7 8 // normal case 9 HashMap<String, List<String>> map = new HashMap<>(); 10 for (String str : strs) { 11 char[] ca = new char[26]; 12 for (char c : str.toCharArray()) { 13 ca[c - 'a']++; 14 } 15 String key = String.valueOf(ca); 16 if (!map.containsKey(key)) { 17 map.put(key, new ArrayList<>()); 18 } 19 map.get(key).add(str); 20 } 21 return new ArrayList<>(map.values()); 22 } 23 }