Array
Hash Table
Sorting
String
Description
Given an array of strings strs
, group the anagrams together. You can return the answer in any order .
Example 1:
Input: strs = ["eat","tea","tan","ate","nat","bat"]
Output: [["bat"],["nat","tan"],["ate","eat","tea"]]
Explanation:
There is no string in strs that can be rearranged to form "bat"
.
The strings "nat"
and "tan"
are anagrams as they can be rearranged to form each other.
The strings "ate"
, "eat"
, and "tea"
are anagrams as they can be rearranged to form each other.
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 lowercase English letters.
Solutions
Solution 1: Hash Table
Traverse the string array, sort each string in character dictionary order to get a new string.
Use the new string as key
and [str]
as value
, and store them in the hash table (HashMap<String, List<String>>
).
When encountering the same key
during subsequent traversal, add it to the corresponding value
.
Take strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
as an example. At the end of the traversal, the state of the hash table is:
key
value
"aet"
["eat", "tea", "ate"]
"ant"
["tan", "nat"]
"abt"
["bat"]
Finally, return the value
list of the hash table.
The time complexity is \(O(n\times k\times \log k)\) , where \(n\) and \(k\) are the lengths of the string array and the maximum length of the string, respectively.
Python3 Java C++ Go TypeScript Rust C#
class Solution :
def groupAnagrams ( self , strs : List [ str ]) -> List [ List [ str ]]:
d = defaultdict ( list )
for s in strs :
k = '' . join ( sorted ( s ))
d [ k ] . append ( s )
return list ( d . values ())
1
2
3
4
5
6
7
8
9
10
11
12 class Solution {
public List < List < String >> groupAnagrams ( String [] strs ) {
Map < String , List < String >> d = new HashMap <> ();
for ( String s : strs ) {
char [] t = s . toCharArray ();
Arrays . sort ( t );
String k = String . valueOf ( t );
d . computeIfAbsent ( k , key -> new ArrayList <> ()). add ( s );
}
return new ArrayList <> ( d . values ());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14 class Solution {
public :
vector < vector < string >> groupAnagrams ( vector < string >& strs ) {
unordered_map < string , vector < string >> d ;
for ( auto & s : strs ) {
string k = s ;
sort ( k . begin (), k . end ());
d [ k ]. emplace_back ( s );
}
vector < vector < string >> ans ;
for ( auto & [ _ , v ] : d ) ans . emplace_back ( v );
return ans ;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13 func groupAnagrams ( strs [] string ) ( ans [][] string ) {
d := map [ string ][] string {}
for _ , s := range strs {
t := [] byte ( s )
sort . Slice ( t , func ( i , j int ) bool { return t [ i ] < t [ j ] })
k := string ( t )
d [ k ] = append ( d [ k ], s )
}
for _ , v := range d {
ans = append ( ans , v )
}
return
}
function groupAnagrams ( strs : string []) : string [][] {
const d : Map < string , string [] > = new Map ();
for ( const s of strs ) {
const k = s . split ( '' ). sort (). join ( '' );
if ( ! d . has ( k )) {
d . set ( k , []);
}
d . get ( k ) ! . push ( s );
}
return Array . from ( d . values ());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 use std :: collections :: HashMap ;
impl Solution {
pub fn group_anagrams ( strs : Vec < String > ) -> Vec < Vec < String >> {
let mut map = HashMap :: new ();
for s in strs {
let key = {
let mut arr = s . chars (). collect :: < Vec < char >> ();
arr . sort ();
arr . iter (). collect :: < String > ()
};
let val = map . entry ( key ). or_insert ( vec! []);
val . push ( s );
}
map . into_iter (). map ( | ( _ , v ) | v ). collect ()
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 using System.Collections.Generic ;
public class Comparer : IEqualityComparer < string >
{
public bool Equals ( string left , string right )
{
if ( left . Length != right . Length ) return false ;
var leftCount = new int [ 26 ];
foreach ( var ch in left )
{
++ leftCount [ ch - 'a' ];
}
var rightCount = new int [ 26 ];
foreach ( var ch in right )
{
var index = ch - 'a' ;
if ( ++ rightCount [ index ] > leftCount [ index ]) return false ;
}
return true ;
}
public int GetHashCode ( string obj )
{
var hashCode = 0 ;
for ( int i = 0 ; i < obj . Length ; ++ i )
{
hashCode ^= 1 << ( obj [ i ] - 'a' );
}
return hashCode ;
}
}
public class Solution {
public IList < IList < string >> GroupAnagrams ( string [] strs ) {
var dict = new Dictionary < string , List < string >> ( new Comparer ());
foreach ( var str in strs )
{
List < string > list ;
if ( ! dict . TryGetValue ( str , out list ))
{
list = new List < string > ();
dict . Add ( str , list );
}
list . Add ( str );
}
foreach ( var list in dict . Values )
{
list . Sort ();
}
return new List < IList < string >> ( dict . Values );
}
}
Solution 2: Counting
We can also change the sorting part in Solution 1 to counting, that is, use the characters in each string \(s\) and their occurrence times as key
, and use the string \(s\) as value
to store in the hash table.
The time complexity is \(O(n\times (k + C))\) , where \(n\) and \(k\) are the lengths of the string array and the maximum length of the string, respectively, and \(C\) is the size of the character set. In this problem, \(C = 26\) .
Python3 Java C++ Go TypeScript
class Solution :
def groupAnagrams ( self , strs : List [ str ]) -> List [ List [ str ]]:
d = defaultdict ( list )
for s in strs :
cnt = [ 0 ] * 26
for c in s :
cnt [ ord ( c ) - ord ( 'a' )] += 1
d [ tuple ( cnt )] . append ( s )
return list ( d . values ())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 class Solution {
public List < List < String >> groupAnagrams ( String [] strs ) {
Map < String , List < String >> d = new HashMap <> ();
for ( String s : strs ) {
int [] cnt = new int [ 26 ] ;
for ( int i = 0 ; i < s . length (); ++ i ) {
++ cnt [ s . charAt ( i ) - 'a' ] ;
}
StringBuilder sb = new StringBuilder ();
for ( int i = 0 ; i < 26 ; ++ i ) {
if ( cnt [ i ] > 0 ) {
sb . append (( char ) ( 'a' + i )). append ( cnt [ i ] );
}
}
String k = sb . toString ();
d . computeIfAbsent ( k , key -> new ArrayList <> ()). add ( s );
}
return new ArrayList <> ( d . values ());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 class Solution {
public :
vector < vector < string >> groupAnagrams ( vector < string >& strs ) {
unordered_map < string , vector < string >> d ;
for ( auto & s : strs ) {
int cnt [ 26 ] = { 0 };
for ( auto & c : s ) ++ cnt [ c - 'a' ];
string k ;
for ( int i = 0 ; i < 26 ; ++ i ) {
if ( cnt [ i ]) {
k += 'a' + i ;
k += to_string ( cnt [ i ]);
}
}
d [ k ]. emplace_back ( s );
}
vector < vector < string >> ans ;
for ( auto & [ _ , v ] : d ) ans . emplace_back ( v );
return ans ;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14 func groupAnagrams ( strs [] string ) ( ans [][] string ) {
d := map [[ 26 ] int ][] string {}
for _ , s := range strs {
cnt := [ 26 ] int {}
for _ , c := range s {
cnt [ c - 'a' ] ++
}
d [ cnt ] = append ( d [ cnt ], s )
}
for _ , v := range d {
ans = append ( ans , v )
}
return
}
function groupAnagrams ( strs : string []) : string [][] {
const map = new Map < string , string [] > ();
for ( const str of strs ) {
const k = str . split ( '' ). sort (). join ( '' );
map . set ( k , ( map . get ( k ) ?? []). concat ([ str ]));
}
return [... map . values ()];
}