leetcode-电话号码的字母组合

607 阅读3分钟

这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战

8月8日,中国队以88枚奖牌收官东京奥运会。虽然最后时刻金牌榜被美国反超,不过已经是很满意的结局了。最后相差这1块金牌,可能会让乒乓球混双项目失去的金牌更加膈应。有时候人生就是这样,虽然准备充分也尽力了,可能一些自己无法把控的外部因素,会让结果走向另外一个方向

昨天立下的Flag,今天算是没倒,出去锻炼了好一会儿,接下来几天继续加油吧。

今天继续刷leetcode的第17题,题目不难,但是看到一个新的解法有点意思。

题目

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

17_telephone_keypad.png

示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

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

示例 3:
输入:digits = "2"
输出:["a","b","c"]

思路

没错,最简单的思路就是直接暴力遍历,对于每个数字,都可能对应3-4个字母,直接暴力枚举就行。不过因为数字长度是不确定的,所以不能直接for,可以写个递归来处理。这种方法没什么可讲的,就是看这个递归怎么写而已,介绍一种另外的思路。
给定了一个数字串之后,可以看作是一个n位的数字,但是不是10进制,是1个3进制或者4进制的数字(7和9可以代表4个字母),每一位上的数值就是代表可选字母的第几个(从第0个开始)。这样的话,可以先计算出所有的可能性M,然后遍历0M-1,代表第0M-1种可能,第k种可能,就把k翻译到这个n位数,这样的话,每1位都会有唯一对应的字母。这里有个技巧是,k翻译成这个n位数的时候,是从最低位开始翻译的,所以最后要把整个字符串反转过来。
下面用 275 来演示一下:
2 -> abc 3种可能
7 -> pqrs 4种可能
5 -> jkl 3种可能
所以总共的可能数 = 3 * 4 * 3 = 36
遍历0~35:
第0种可能,翻译成3为数 000,对应的字符串为apj
第1种可能,翻译成3为数 001,对应的字符串为apk
第2种可能,翻译成3为数 002,对应的字符串为apl
第3种可能,翻译成3为数 010,对应的字符串为aqj
...
第12种可能,翻译成3为数 100,对应的字符串为bpj
...
第35种可能,翻译成3为数 232,对应的字符串为csl

Java版本代码

class Solution {
    public List<String> letterCombinations(String digits) {
        Map<Character, String> map = new HashMap<>();
        map.put('2', "abc");
        map.put('3', "def");
        map.put('4', "ghi");
        map.put('5', "jkl");
        map.put('6', "mno");
        map.put('7', "pqrs");
        map.put('8', "tuv");
        map.put('9', "wxyz");
        List<String> ans = new ArrayList<>();
        int len = digits.length();
        if (len == 0) {
            return ans;
        }
        // 总共可能的情况数
        int count = 1;
        for (int i = 0; i < len; i++) {
            char digChar = digits.charAt(i);

            
            String letters = map.get(digChar);
            count *= letters.length();
        }
        // 这是一个三进制或者四进制的数,不同的位可能进制不同
        for (int index = 0; index < count; index++) {
            StringBuilder item = new StringBuilder();
            int lastNum = index;
            // 这里因为从低位开始计算比较方便,所以要反过来
            for (int j = len-1; j >=0; j--) {
                char digChar = digits.charAt(j);
                String letters = map.get(digChar);
                int lettersLen = letters.length();
                item.append(letters.charAt(lastNum % lettersLen));
                lastNum /= lettersLen;
            }
            // 别忘记这里把字符串再反转过来
            ans.add(item.reverse().toString());
        }
        return ans;
    }
}