题目描述

✅ 面试题 16.02. 单词频率

image-20260929105832595

题意分析

用 book 构造一个词频查询对象,之后可多次调用 get(word),返回该单词在书中出现的总次数,没有出现则返回 0。查询只读取统计结果,不会消耗或减少次数。

解法:哈希表统计词频(构造一次,多次查询)

核心思路

[!blue]

书的内容在构造后固定,每个单词的频次也随之固定。与其每次查询都遍历原数组,不如构造时一次统计完整频次,让所有查询复用同一张表。

用 freq[word] 保存已经读过的部分中该单词的出现次数。初始没有读入任何单词,所有次数视为 0;每读到一次 word 就将其计数加 1,因此完整遍历后,表中的值恰好是全书词频。

查询直接返回对应计数。Java 使用 getOrDefault(word, 0),避免不存在的键得到空值;Go 读取不存在的整数映射键会得到零值 0。两者都自然覆盖未出现的单词,不必额外保存原书或为每次查询更新映射。

解题步骤

  1. 构造对象时初始化空频次表。
  2. 遍历 book 中的每一次单词出现,在原频次上加 1。
  3. 将频次表保存在对象中,供所有后续查询使用。
  4. 查询时返回对应频次,不存在则返回 0;重复查询同一个词会得到相同结果。

代码实现

class WordsFrequency {
    private final Map<String, Integer> freq = new HashMap<>();

    public WordsFrequency(String[] book) {
        for (String word : book) {
            // 构造时累加所有出现次数,查询时无需重扫书本。
            freq.put(word, freq.getOrDefault(word, 0) + 1);
        }
    }

    public int get(String word) {
        return freq.getOrDefault(word, 0);
    }
}
type WordsFrequency struct {
    freq map[string]int
}

func Constructor(book []string) WordsFrequency {
    f := make(map[string]int)
    for _, w := range book {
        // 构造时累加所有出现次数,查询时无需重扫书本。
        f[w]++
    }
    return WordsFrequency{freq: f}
}

func (wf *WordsFrequency) Get(word string) int {
    return wf.freq[word]
}

复杂度分析

设书中单词的总字符数为 S,不同单词数为 u,查询单词长度为 L。

  • 时间复杂度:构造期望 $O(S)$,单次查询期望 $O(L+1)$,包含字符串哈希和比较的代价。
  • 空间复杂度:不计输入字符串内容,频次表占 $O(u)$。表中保存字符串引用或字符串头及计数,不复制整本书的字符内容。

关键点总结

[!green]

  • 固定数据上的多次查询,可以共用一次预处理结果。
  • 每个单词出现一次就累计一次,映射中只有一个对应键。
  • 构造后频次不再改变,查询缺失键按零处理。

易错点总结

[!yellow]

  • 每次出现都直接写入 1,会覆盖已有次数,丢失重复出现的信息。
  • Java 直接返回可能为 null 的 Integer,自动拆箱时会失败,应提供默认零。
  • 只保存出现多次的单词,会漏掉词频为 1 的合法结果。
  • Go 必须先初始化映射再累加,不能向未初始化的映射写入。

相似题目

题目 难度 关联与区别
692. 前K个高频单词 中等 同样先统计完整词频,原题还排序取TopK,本题只按键返回次数。
244. 最短单词距离 II 中等 同样一次预处理供多次查询,原题需保存位置列表,本题只需保存频次。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2024/23294859
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!