原题:
Given an array of strings, group anagrams together.
For example, given: ["eat", "tea", "tan", "ate", "nat", "bat"]
,
Return:
[ ["ate", "eat","tea"], ["nat","tan"], ["bat"] ]
Note: All inputs will be in lower-case.
即对给定字符串数组进行分类,把由相同字母(字母和字母数量都相同)的字符串分到一类。
思考过程:
对于这种需要进行比较的问题,用人脑解决需要记忆,那用程序解决会用到哈希表。一开始想麻烦了,是一个超时的思路:每类字符串对应一个哈希表。大致上是遍历整个字符串数组,将字符串与现在已经有的哈希表比较,如果和哪个哈希表匹配上了(字母和字母数量都匹配上),那就把它放到这个哈希表对应的字符串集合里。这里遇到很多问题,比如和哈希表进行匹配时,要匹配字母和字母数量,就不容易记录。我用的办法是哈希表里key为char,value为int表示该char在此哈希表出现的次数。那最后匹配完还要保证哈希表所有的字符都恰好匹配,也就是遍历哈希表,看它是否所有元素value为0。最后终于没有bug了,当然超时了。时间复杂度O(s^2)(s表示字符串数组字符个数)。代码如下:
public List<List<String>> groupAnagramsTLE(String[] strs) {
if (strs.length == 0) return null;
List<List<String>> ret = new ArrayList<>();
List<Map<Character,Integer>> maps = new ArrayList<>();
Map<Character,Integer> map = new HashMap<>();//哈希表存的是这个字符串有哪些字符以及有多少个
int len = strs[0].length();
mapsPutString(maps,strs[0]);//初始化List<Map>
listAddString(ret,strs[0]);//初始化结果集
for (int i = 1;i < strs.length;i++){//i表示字符串序号
boolean isGroupExist = false;//判断是否有能匹配的,如果没有,新建关于它的哈希表
String s = strs[i];
for (int k = 0; k < maps.size(); k++) {//k表示哈希表序号
if (s.length() < maps.get(k).size()) continue;//提前过滤一下
boolean isMatch = true;//判断第i个字符串和第k个哈希表是否匹配
Map<Character,Integer> hasMap1 = new HashMap<>();
hasMap1.putAll(maps.get(k));
for (int j = 0;j < s.length();j++){//j表示字符序号
char c = s.charAt(j);
if (!hasMap1.containsKey(c) || hasMap1.get(c) == 0) isMatch = false;
else hasMap1.put(c,hasMap1.get(c) - 1);
}
for (Map.Entry<Character,Integer> entry : hasMap1.entrySet())//遍历哈希表确保哈希表中所有元素都匹配过了。
if (entry.getValue() != 0) isMatch = false;
if (isMatch){
ret.get(k).add(s);
isGroupExist = true;
break;
}
}
if (!isGroupExist) {
mapsPutString(maps,s);
listAddString(ret,s);
}
}
return ret;
}
后来知道可以将每个字符串排序(按字母表顺序),哈希表里存储字符串就可以了,每次匹配时只需将字符串排序,看哈希表里是否有排好序的字符串就可以了。我算了一下时间复杂度为O(sbloga)(s是字符串数组字符个数,a是每个字符串字符个数,b是字符串个数,a*b = s),accepted了。解题思路见代码注释。
结果代码:
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> ret = new ArrayList<>();
Map<String,Integer> hasMap = new HashMap<>();
for (int i = 0,j = 0;i < strs.length;i++){//i用来遍历字符串数组,j用来标记哈希表中的字符串对应结果集的索引。
String str = strs[i];
char[] chars = str.toCharArray();//之所以这么做是因为Arrays.sort()只能对数组进行操作
Arrays.sort(chars);
String s = String.valueOf(chars);
if (hasMap.containsKey(s))//如果哈希表里有,加入结果集
ret.get(hasMap.get(s)).add(str);
else {//哈希表里没有,将str存入哈希表,value为j(j - 1表示哈希表里之前有多少个字符串)
List<String> strings = new ArrayList<>();
strings.add(str);ret.add(strings);
hasMap.put(s,j);
j++;
}
}
return ret;
}