top1编程
← 返回题目
题解

机器翻译(NOIP)

1 条题解

  • 0
    @ 2026-8-6 2:18:11

    P4853 机器翻译(NOIP)(基础)

    解题思路

    第一步,理解题意。 翻译软件有一个内存,里面有 M 个单元,每个单元能放一个单词。翻译一篇文章时,每遇到一个单词:如果内存里已经有这个单词,就直接翻译,不用查词典;如果内存里没有,就要去外存的词典里查一次,然后把这个单词放进内存。内存满了 M 个单词时,会清掉"最早进入"的那个单词,腾出位置放新单词。问整个文章翻译下来,一共查了几次词典。

    第二步,类比生活。 就像你书包里只能装 M 本小词典。看书遇到不认识的单词,先翻书包:书包里有就直接用;没有就去书架找(算一次"查词典"),找到后放进书包。书包装满了,就把最旧的那本拿出来再装新的。

    第三步,用队列模拟内存。 队列"先进先出",正好符合"最早进入的先被清掉"的规则。用数组 q 当队列,队首 head 指向最早进入的单词,队尾 tail 指向新单词放的位置。再用一个标记数组 in[],in[x]=1 表示单词 x 现在在内存里(单词大小不超过 1000,数组开 1005 就够)。

    第四步,逐个单词处理。 读入文章的第 x 个单词:如果 in[x]==1,说明内存里有,直接跳过;否则查词典次数加 1,然后看内存:如果没满(tail-head < M),直接把 x 放到队尾;如果满了,就把队首的单词清掉(in[q[head]]=0,head 加 1),再把 x 放到队尾。用一个变量 ans 累加查词典次数,最后输出 ans。

    第五步,边界情况。 M 最小是 1,内存只有一个单元,每来一个新单词都要把旧的踢掉;连续出现相同的单词时,第二次就不查词典了。举个例子:M=3,文章是 1 2 1 5 4 4 1。过程是:查 1、查 2、1 在内存、查 5、查 4(内存满踢掉 1)、4 在内存、查 1(踢掉 2),总共查 5 次,和样例一致。

    参考代码

    // 机器翻译(NOIP):内存M个单元,FIFO缓存,统计查词典次数
    #include <iostream>
    using namespace std;
    int main() {
        int m, n;
        cin >> m >> n;
        int q[1005];          // 内存队列,按存入顺序存放单词
        int in[1005] = {0};   // in[x]=1 表示单词 x 已在内存
        int head = 0, tail = 0;
        int ans = 0;          // 查词典次数
        for (int i = 0; i < n; i++) {
            int x;
            cin >> x;
            if (in[x]) continue;    // 内存有,不用查词典
            ans++;                  // 内存没有,去词典查
            if (tail - head < m) {  // 内存还没满
                q[tail++] = x;
                in[x] = 1;
            } else {                // 内存已满,清掉最早进入的
                in[q[head]] = 0;
                head++;
                q[tail++] = x;
                in[x] = 1;
            }
        }
        cout << ans << endl;
        return 0;
    }
    

    复杂度分析

    时间复杂度:每个单词只处理一次,所以是 O(N),N 最大和文章长度相同,非常高效。

    空间复杂度:队列长度不超过 M,标记数组大小取决于单词最大值(不超过 1000),所以是 O(M + 1000)。

    • 1