LeetCode 49.字母异位词分组

138 阅读1分钟

「这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战」。

题目:给定一个字符串,将字符串中字母异位词进行分组,可以按任意顺序分组。所谓的字母异位词,是指字符串顺序存在变化但字符串的组成相同,并且字符串中每个字母只使用一次。

解题思路

本题的关键是理解何为字母异位词,实际上字母异位词的关键是所有词按照字典序排序都是相同的,理解了这一点则可以轻松解决,可以遍历字符串中所有字符串,字符串转为字符数组,之后将每个字符进行字典序排序作为key,如果两个字符的key相同,则必定属于字符异位词,此时如何存储呢?

既然已经提到了key,那Java中一个简单的数据结构Map可以轻松解决,将每个字符的字典序作为keyvalue是一个ArrayList,之后如果存在字符的排序在此Map,则判定为字母异位词,将其加入ArrayList中即可,将Map中的值取出来保存至一个新的ArrayList中即可,代码如下:

public List<List<String>> groupAnagrams(String[] strs) {
        HashMap<String, ArrayList<String>> map = new HashMap<>();
        for(String s:strs){
            char[] ch = s.toCharArray();
            Arrays.sort(ch);
            String key = Arrays.toString(ch);
            if(!map.containsKey(key)){
                map.put(key, new ArrayList<String>());
            }
            map.get(key).add(s);
        }
        return new ArrayList<>(map.values());
    }

该方法的时间复杂度为O(N)O(N),空间复杂度取决于实际情况。

计数法

另一种方法和上述方式类似,都是将最终的值保存在map中,所不同的是两种方法存放的键的构成不同,如果两个字符串是字母异位词,那么其构成必然相同,例如对于aheeha,其都是由一个a一个h和一个e构成的,此时只需将a1e1h1作为键即可,取得此键之后后面操作就和上面方法类似,如何保证按既定顺序获得这些键是一个问题。

解决方法就是我们可以新建一个长度为26位的数组,因为可能出现的字符只有26个,这个数组中存放的就是每个字符出现的次数,之后遍历数组,取出数组内容即可,代码如下:

public List<List<String>> groupAnagrams(String[] strs) {
        HashMap<String, ArrayList<String>> map = new HashMap<>();
        for(String s:strs){
            int[] ints = new int[26];
            for(int i=0;i<s.length();i++){
                ints[s.charAt(i)-'a']++;
            }
            StringBuilder sb = new StringBuilder();
            for(int i=0;i<26;i++){
                if(ints[i]!=0){
                    sb.append((char)'a'+i);
                    sb.append(ints[i]);
                }
            }
            if(!map.containsKey(sb.toString())){
                map.put(sb.toString(), new ArrayList<String>());
            }
            map.get(sb.toString()).add(s);
        }
        return new ArrayList<>(map.values());
    }