找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3680

积分

0

好友

492

主题
发表于 1 小时前 | 查看: 2| 回复: 0

北京这地方,真能把中年人的体面一点点磨掉。

44 岁,财务总监,十多年经验,按理说不算差吧?放在公司里,能管账、能控风险、能扛审计,怎么也不是新人能随便替的。

可一到招聘市场,味儿就变了。求职 时 HR 一看年龄,先沉默两秒,后面什么经验、项目、证书,好像都得打个折。

40岁财务总监求职困境的社交媒体截图

高管岗少,企业又想要便宜、年轻、能熬的。北京机会多是真的,卷起来也是真的狠。你以为自己在选公司,结果发现公司也在筛年龄,筛行业,筛成本。

她甚至开始想,实在不行就离开北京。一个人在一座城熬了这么多年,最后被迫考虑退场,多少有点难受。

面试题:词典中最长的单词

这题看着像在数组里挑一个最长字符串,手一快就会写成 contains() 判断。

然后就挂。

输入是这样的:

["w","wo","wor","worl","world"]

答案是 world,没问题。

但换成:

["a","banana","app","appl","ap","apply","apple"]

答案不是 banana,而是 apple

原因很简单,题目要的不是“词典里最长的单词”,而是“能从一个字母开始,每次加一个字母,并且每一步都在词典里出现过”的最长单词。

也就是说,apple 要成立,必须有:

a
ap
app
appl
apple

少一个都不行。

这个条件很容易被写歪。我见过有人直接判断:

word.contains(prefix)

这就离谱了。contains 看的是子串,不是前缀链。还有人每个单词都从头截一遍,然后去数组里找,能过小数据,一上量就开始慢。

我一般会用 HashSet 做。原因不复杂:这题查存在性特别多,别拿 List 硬扫。

关键判断就一句:一个单词能进候选,前提是它去掉最后一个字符之后,已经是一个合法单词。

比如 apple 能不能用,不用从 a 一直查到 appl,只要排序后保证短词在前面,判断 appl 合法就够了。这道题在 LeetCode 上其实就是 720 号“词典中最长的单词”,HashSet 加排序的思路比建 Trie 更直接。

代码可以这么写:

import java.util.*;

public class LongestDictionaryWord {

    public String findLongest(String[] words) {
        if (words == null || words.length == 0) {
            return "";
        }

        Arrays.sort(words, (left, right) -> {
            if (left.length() != right.length()) {
                return left.length() - right.length();
            }
            return left.compareTo(right);
        });

        Set<String> ready = new HashSet<>();
        String answer = "";

        for (String word : words) {
            if (word == null || word.isEmpty()) {
                continue;
            }

            boolean canBuild = word.length() == 1
                    || ready.contains(word.substring(0, word.length() - 1));

            if (!canBuild) {
                continue;
            }

            ready.add(word);

            if (word.length() > answer.length()) {
                answer = word;
            }
        }

        return answer;
    }
}

这段代码里最关键的不是 HashSet,而是排序。

先按长度升序排,保证短词先处理。长度一样的时候按字典序排,这样有两个同长度答案时,先遇到的一定是字典序更小的那个。

所以更新答案时只需要判断:

if (word.length() > answer.length()) {
    answer = word;
}

不用再写一堆:

if (word.length() == answer.length() && word.compareTo(answer) < 0)

不是不能写,是没必要。前面排序已经把这个活干了。

拿刚才那组数据走一遍:

a      -> 合法,加入 ready
ap     -> 前缀 a 在 ready,合法
app    -> 前缀 ap 在 ready,合法
appl   -> 前缀 app 在 ready,合法
apple  -> 前缀 appl 在 ready,合法
apply  -> 前缀 appl 在 ready,合法
banana -> 前缀 banan 不在 ready,丢掉

最后答案还是 apple,因为 appleapply 一样长,但 apple 字典序更小,排序时它先出现。

这里有个小坑:不要在遍历时直接把所有单词都塞进 ready

像这样就错了:

Set<String> all = new HashSet<>(Arrays.asList(words));

然后对每个单词检查所有前缀。虽然也能做,但你会多扫很多次字符串。

更麻烦的是,有些人写着写着就变成只判断“前缀存在于原数组”,忘了判断“前缀本身也必须能一步步构造出来”。

比如:

["a", "abc", "abcd"]

abcd 不能算合法。因为 ab 不存在,链断了。

如果用上面的 ready 思路,就不会误判。abc 的前缀 ab 不在 ready,所以 abc 进不去。abcd 的前缀 abc 也进不去。

这种题我不太喜欢一上来就 Trie。

Trie 能做,但对这题有点重。你要建节点、标记结尾、DFS,还要控制字典序。写出来没问题,就是代码量多,现场面试或者笔试里更容易手滑。

除非题目后面追问:“词典会频繁新增,频繁查询最长可构造单词”,那 Trie 才更像个长期方案。

普通版本,用排序加 Set 就够了。

复杂度也很干净。排序是 O(n log n),每个单词只截一次前缀,整体可以看成和字符串总长度相关。空间就是一个 HashSet,存放已经验证通过的单词。

最后可以补个测试:

public static void main(String[] args) {
    LongestDictionaryWord tool = new LongestDictionaryWord();

    String[] words = {
        "a", "banana", "app", "appl", "ap", "apply", "apple"
    };

    System.out.println(tool.findLongest(words)); // apple
}

这题真正要盯住的不是“最长”,而是“每一层都得站得住”。

最长只是结果。

前缀链才是门槛。




上一篇:向量加法入门:三角形法则、多边形法则与合向量计算解析
下一篇:Kooky开源AI终端:一站式管理 Claude Code、Codex、Gemini CLI 的总控台(macOS)
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-6-1 04:31 , Processed in 0.747988 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表