题解:洛谷 B3927 小杨的字典
【题目来源】
洛谷:B3927 [GESP202312 四级] 小杨的字典 - 洛谷
【题目描述】
在遥远的星球,有两个国家 A 国和 B 国,他们使用着不同的语言:A 语言和 B 语言。小杨是 B 国的翻译官,他的工作是将 A 语言的文章翻译成 B 语言的文章。
为了顺利完成工作,小杨制作了一本字典,里面记录了 \(N\) 个 A 语言单词对应的 B 语言单词,巧合的是,这些单词都由地球上的 26 个小写英文字母组成。
小杨希望你写一个程序,帮助他根据这本字典翻译一段 A 语言文章。这段文章由标点符号 !()-[]{}\|;:'",./?<> 和一些 A 语言单词构成,每个单词之间必定由至少一个标点符号分割,你的程序需要把这段话中的所有 A 语言单词替换成它的 B 语言翻译。特别地,如果遇到不在字典中的单词,请使用大写 UNK 来替换它。
例如,小杨的字典中包含 \(2\) 个 A 语言单词 abc 和 d,它们的 B 语言翻译分别为 a 和 def,那么我们可以把 A 语言文章 abc.d.d.abc.abcd. 翻译成 B 语言文章 a.def.def.a.UNK. 其中,单词 abcd 不在词典内,因此我们需要使用 UNK 来替换它。
【输入】
第一行一个整数 \(N\),表示词典中的条目数。保证 \(N \le 100\)。
接下来 \(N\) 行,每行两个用单个空格隔开的字符串 \(A\),\(B\) ,分别表示字典中的一个 A 语言单词以及它对应的 B 语言翻译。保证所有 \(A\) 不重复;保证 \(A\) 和 \(B\) 的长度不超过 \(10\)。
最后一行一个字符串 \(S\) ,表示需要翻译的 A 语言文章。保证字符串 \(S\) 的长度不超过 \(1000\),保证字符串 \(S\) 只包含小写字母以及标点符号 !()-[]{}\|;:'",./?<>。
【输出】
输出一行,表示翻译后的结果。
【输入样例】
2
abc a
d def
abc.d.d.abc.abcd.
【输出样例】
a.def.def.a.UNK.
【核心思想】
-
问题分析:给定一个字典(A 语言单词到 B 语言翻译的映射)和一段 A 语言文章,需要将文章中的所有 A 语言单词替换为对应的 B 语言翻译,不在字典中的单词替换为
UNK。单词之间由标点符号分隔。这是一个字符串解析 + 哈希映射问题,核心在于正确识别单词边界并进行字典查找替换。 -
算法选择:
- 哈希映射(
map/unordered_map):建立 A 语言单词到 B 语言翻译的快速查找 - 逐字符解析:遍历文章,区分小写字母(单词字符)和标点符号(分隔符)
- 哈希映射(
-
关键步骤:
- 读入字典:读取 \(N\),然后 \(N\) 对 \((A, B)\),存入
mp[A] = B - 读入文章:字符串 \(S\)
- 逐字符解析(\(i\) 从 \(0\) 到 \(S.size()-1\)):
- 若 \(S[i]\) 是小写字母:\(t \leftarrow t + S[i]\)(累积当前单词)
- 否则(标点符号):
- 若 \(t \neq ""\):查找字典,输出翻译或
UNK,清空 \(t\) - 输出当前标点符号 \(S[i]\)
- 若 \(t \neq ""\):查找字典,输出翻译或
- 处理末尾单词:循环结束后若 \(t \neq ""\),进行最后的字典查找和输出
- 读入字典:读取 \(N\),然后 \(N\) 对 \((A, B)\),存入
-
时间/空间复杂度:
- 时间复杂度:\(O(N \cdot L + |S|)\),\(L\) 为单词最大长度,字典插入 \(O(N \cdot L)\),文章解析 \(O(|S|)\)
- 空间复杂度:\(O(N \cdot L)\),字典存储
-
字符串解析与哈希映射的核心思想:
- 状态机思想:解析过程可视为两种状态的切换——"在单词中"(累积字母)和"在分隔符中"(输出标点)。遇到非字母字符时触发单词结束事件
- 哈希查找优化:使用
map实现 \(O(\log N)\) 或unordered_map实现 \(O(1)\) 的单词查找,避免线性扫描字典 - 边界处理:文章末尾可能以单词结尾(无尾随标点),需要在循环结束后单独处理剩余单词
- 标点符号的原样保留:非小写字母字符直接输出,保持文章结构不变
- 适用于文本替换、词法分析类基础字符串处理问题
【算法标签】
普及- #字符串入门
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n; // 字典条目数
map<string, string> mp; // 字典:单词 -> 翻译
int main()
{
// 输入字典大小
cin >> n;
// 读入字典
for (int i = 1; i <= n; i++)
{
string a, b;
cin >> a >> b; // a: 单词, b: 翻译
mp[a] = b; // 建立映射
}
// 输入待翻译的字符串
string s;
cin >> s;
string t = ""; // 临时存储当前识别的单词
int i;
// 遍历输入字符串
for (i = 0; i < s.size(); i++)
{
// 如果是小写字母,加入到当前单词
if (islower(s[i]))
{
t += s[i];
}
else
{
// 遇到非小写字母,表示单词结束
if (t != "")
{
// 查找字典
if (mp.count(t))
{
cout << mp[t]; // 输出翻译
}
else
{
cout << "UNK"; // 未知单词
}
t = ""; // 清空临时单词
}
// 输出当前的非字母字符(保持原样)
cout << s[i];
}
}
// 处理末尾可能剩余的单词
if (t != "")
{
if (mp.count(t))
{
cout << mp[t];
}
else
{
cout << "UNK";
}
}
// 问题:这里输出s[i]会越界,因为循环结束i==s.size()
// 应该是想要输出最后一个字符?但实际不应该在这里输出
// cout << s[i]; // 这行应该删除
return 0;
}
【运行结果】
2
abc a
d def
abc.d.d.abc.abcd.
a.def.def.a.UNK.
浙公网安备 33010602011771号