紫书刷题进行中,题解系列点这里
习题4-6 UVA508 Morse Mismatches(38行AC代码)
思路分析
已知字符编码表,接受的单词表(context),现给出摩尔斯电码,根据规则解码。规则如下:
- 1个精确匹配:直接输出
- 多个精确匹配:输出字典序最小/context中最前面的都对(原题描述有些歧义fewest character长度最短?)
- 0个精确匹配:找出最小模糊匹配,即两个字符串存在这样的关系:
a是b的子串/b是a的子串
,且二者长度相差最小(若有多个满足,输出任意单词即可)
算法设计
-
惯性思维:想直接从摩尔斯密码来解码,那么必须dfs许多情况,过于复杂。
-
逆向思维:根据给定的字符编码表和context,可以计算出每个单词的摩尔斯编码,到时直接进行匹配即可
因此,定义string codetbl[200]
存储字符编码表,其中code[ch]
表示字符ch对应的编码串。
定义map<string, vector<string> > ctxt;
,其中ctxt[s]
表示摩尔斯编码串s对应的可能单词,单词在vector中按输入顺序存放,若改为set,则按字典序排列
接着依次输入摩尔斯密码,根据解码规则处理即可。
注意点
- 题意不明,经测试,多个精确匹配可输出字典序最小/context中最前面的
- 模糊匹配一定存在,因为题目仅考虑在字符串末尾增加或减少若干个字符
AC代码(C++11,解密模拟,逆向思维,map)
#include<bits/stdc++.h>
using namespace std;
string codetbl[200], code, s;
char ch;
// map<string, set<string> > ctxt; // 上下文,字典序排列
map<string, vector<string> > ctxt; // 上下文,输入顺序排列
int main() {
while (cin >>ch && ch != '*') {
cin >>code; codetbl[ch] = code;
}
while (cin >>s && s != "*") {
string st;
for (auto c : s) st += codetbl[c];
// ctxt[st].insert(s); // 字典序排列
ctxt[st].push_back(s);
}
while (cin >>s && s != "*") {
if (ctxt.find(s) != ctxt.end()) { // 存在
cout <<*ctxt[s].begin();
if (ctxt[s].size() > 1) cout <<"!";
}
else { // 不存在
int minlen = 0x3ffff; string ans;
for (auto p : ctxt) {
if (s == p.first.substr(0,s.size()) || p.first == s.substr(0,p.first.size())) { // 前缀
int i = abs((long)p.first.size()-(long)s.size());
if (minlen >= i) {
minlen = i;
ans = *p.second.begin();
}
}
}
cout <<ans<<"?";
}
puts("");
}
return 0;
}