leetcode-字母异位词分组

106 阅读2分钟

2022年第一个工作日,虽然浪费了一些时间,但是基本还是按照计划完成了该完成的事情,每天做事的一些计划性,应该是今年需要再加强的方面,想到哪做到哪儿确实效率堪忧。

今天的题目挺简单的,可能把算法题分为简单、中等、困难3个难度等级还是有点太宽泛了,有时候遇到的中等难度的题目还觉得挺难的。

题目

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。

示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:
输入: strs = [""]
输出: [[""]]

示例 3:
输入: strs = ["a"]
输出: [["a"]]

思路

首先要理解字母异位词,翻译成自己的话,包含的字符是一样的,但是顺序不同。所以,可以先想象我们有一些桶,每个桶可以装1组字母异位词,要做的就是把单词丢到对应的桶里面。这样的话,我们的问题就转成了,怎么标记一个桶,让后面的字符串知道是否归属这个桶。比较容易能想到的是,对字符串内部的字符进行字典排序,字母异位词排序后的字符串肯定是一样的,就可以作为桶的标志。这样应该可以解,但是我们知道,排序是比较耗时的操作,每个字符串排序的时间复杂度O(klogk),k是字符串长度,这样做可能会TLE。另一个思路时,由于单词只能包含字母,只有26个英文字母,是有限可枚举的,所以可以组成这样一个字符串作为桶的标志,这个字符串有26个部分组成,每个部分都由 字母+数量这样的单元构成,类似:a0b1c2...z0。一个显而易见的优化是,我们可以把数量为0的单元去掉,就会形成一个类似 b1c2这样的键。由于只是把字符串中字符遍历一遍,这里的时间复杂度是O(k)。
实例1的桶和每个桶的标志如下图所示:

image.png

Java版本代码

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<>();
        for (String item : strs) {
            int[] counts = new int[26];
            for (char c : item.toCharArray()) {
                counts[c-'a']++;
            }
            StringBuilder keyBuilder = new StringBuilder();
            for (int i = 0; i < 26; i++) {
                if (counts[i] > 0) {
                    keyBuilder.append('a' + i);
                    keyBuilder.append(counts[i]);
                }
            }
            String key = keyBuilder.toString();
            List<String> list = map.getOrDefault(key, new ArrayList<>());
            list.add(item);
            map.put(key, list);
        }
        List<List<String>> result = new ArrayList<>();
        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
            result.add(entry.getValue());
        }
        return result;
    }
}