程序员老鬼

老板裁员后奇怪:原先,100个人干50个人的活;裁掉一半后,剩下50人干25个人的活,好像效率并没有提升。

刚刷到这个,给我看愣了。

老板原本以为,裁掉一半人,剩下的人为了保住饭碗,多少得卷起来吧。结果呢,之前100个人磨磨蹭蹭干50个人的活,现在50个人照样磨磨蹭蹭,只干出25个人的量。

Image

老板估计也纳闷:人少了,工资省了,怎么效率一点没涨?

这其实挺正常。活多不多先不说,裁员这一下,大家都看明白了。干得快不代表安全,干得多也不一定加钱,甚至可能因为你能干,活全塞你头上。

那还拼什么。剩下的人每天最重要的事,估计就是别太突出,也别太落后,稳稳混着。然后老板还在会议室研究,怎么团队突然没冲劲了……

今日面试题

一道“唯一摩尔斯密码词”,代码不难,去重这一步别绕远了

给一组英文单词,把每个字母替换成对应的摩尔斯密码,最后问:一共能得到多少种不同的转换结果。

这题第一眼看着像字符串拼接,实际上真正要处理的只有两件事:字符映射和结果去重。代码写长了,八成是方向绕了。

26 个小写字母都有固定的摩尔斯编码,例如:

a -> .-
b -> -...
c -> -.-.

假设单词是 cab,转换结果就是:

-.-..--...

这里不需要在不同字母的编码之间加分隔符。题目已经规定了转换方式,我们只管按照字符顺序拼起来。

麻烦点在后面:两个不同的单词,转换后的字符串可能完全一样。既然只统计不同结果,拿一个 Set 存最省事。转换一次放进去一次,重复数据自然会被挡掉。

Java 代码我一般会这样写:

import java.util.HashSet;
import java.util.Set;

publicclassMorseWordCounter{

privatestaticfinal String[] MORSE_TABLE = {
".-", "-...", "-.-.", "-..", ".", "..-.", "--.",
"....", "..", ".---", "-.-", ".-..", "--", "-.",
"---", ".--.", "--.-", ".-.", "...", "-", "..-",
"...-", ".--", "-..-", "-.--", "--.."
    };

publicintcountDifferentCodes(String[] words){
        Set<String> codeSet = new HashSet<>();

for (String word : words) {
            StringBuilder code = new StringBuilder();

for (int i = 0; i < word.length(); i++) {
int tableIndex = word.charAt(i) - 'a';
                code.append(MORSE_TABLE[tableIndex]);
            }

            codeSet.add(code.toString());
        }

return codeSet.size();
    }
}

这段代码里有一行是关键:

int tableIndex = word.charAt(i) - 'a';

字符可以参与整数运算。'a' - 'a' 等于 0,'b' - 'a' 等于 1,一直到 'z' - 'a' 等于 25,正好对应摩尔斯编码数组的下标。

这里没必要再写一串 if 或者 switch。那种代码不但长,漏掉一个字母还不好查。

我看这题时还会顺手检查一个细节:不要把所有单词共用同一个 StringBuilder,除非每轮都明确清空。否则前一个单词的编码会残留到后一个单词里,结果看着能运行,数据却已经串了。直接在外层循环里创建新的 StringBuilder,代码更稳,也没必要为这点对象创建做所谓的“优化”。

设所有单词的字符总数为 n,每个字符只转换一次,所以时间复杂度是 O(n)。Set 中需要保存转换后的字符串,空间复杂度同样与转换结果总长度有关,可以看作 O(n)。

这题不需要回溯,也不需要动态规划。逐个转换,扔进 Set,最后返回集合大小就结束。真正值得记住的不是摩尔斯密码,而是这种固定映射加唯一性统计的题型:数组负责查表,Set负责去重,别再额外造一层复杂逻辑。