题解 UVA508 【摩尔斯密码 Morse Mismatches】

· · 题解

这个题当把细节理解清楚了以后,其实很简单,就是因为自己题意理解不清,对题目的曲解太大了,所以才会拖延了这么长时间,看来以后需要耐心仔细地读英文题了。。。这题的意思是给定一些莫尔斯编码,给定一些已知字典,给定一些编码,求解这些编码的对应原文,如果可以精确匹配,则直接输出原单词,如果有多个可精确匹配的结果,则输出匹配结果里字典序最小的单词(紫书上说输出任意一个,这是错误的)并在末位加上“!”;如果无法精确匹配,则可以在编码的末尾增加或删除一些字符后匹配单词(增删应尽量小,就是找到增删最少的模糊匹配),无论增删后匹配一个还是多个,最后都要加上“?”,如果有多个模糊匹配结果(增删数相等),则输出字典序最小的匹配结果;如果无精确匹配也无模糊匹配结果,则输出整个给定字典里字典序最小的那个单词。

实现方面,看网上标程是用映射写的(也难怪自己没发现字典序这个关键词,映射里的元素直接就是排好序的,用映射看来很简单啊。。。= =),我用的是简单的字符数组,一个用来存给定的莫尔斯电码,一个结构体包含两个字符数组,一个存给定单词明文,并存下按莫尔斯电码翻译后的密文,重载小于运算,使字典按字典序排好,对于每个给定的编码,匹配字典里所有单词的密文,判断精确匹配的个数,若为0则开始模糊匹配,代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
struct text{
    char Plaintext[20];//明文
    char Ciphertext[100];//密文
    bool operator < (const text a)const{
        return strcmp(Plaintext,a.Plaintext)<0;//重载小于运算,将给定字典元素按字典序排序
    }
}str[101];
char morse[128][10],code[101];//morse存储摩尔斯码,code存储待解编码
int n=0;
int toMatch(int &p);//传入p的引用,获得匹配数并使p指向匹配位置
int main(){
    ios::sync_with_stdio(false);
    char c;
    while(cin>>c,c!='*')//输入摩尔斯码
        cin>>morse[(int)c];
        while(cin>>str[n].Plaintext,str[n].Plaintext[0]!='*'){//输入给定字典元素
            int len=strlen(str[n].Plaintext);
            str[n].Ciphertext[0]=0;
            for(int i=0;i<len;i++)//将给定字典元素按摩尔斯码翻译成密文存储
                strcat(str[n].Ciphertext,morse[(int)str[n].Plaintext[i]]);
            n++;
    }
    sort(str,str+n);//按字典序排序
    while(cin>>code,code[0]!='*'){
        int p,cont=toMatch(p);//获得匹配数量
        if(cont)
            cout<<str[p].Plaintext<<(cont==1?"":"!")<<endl;//多个精确匹配要额外输出叹号
        else{
            int minn=10000,pi=-1,lencode=strlen(code);
            for(int i=0;i<n;i++){//模糊匹配时,遍历字典集
                int len=strlen(str[i].Ciphertext);
                if(lencode>len&&(strstr(code,str[i].Ciphertext)==code){//如果字典里某元素是待解编码的子串,且匹配位置为待解编码首字符,说明该字典元素可由待解编码删减字符得到
                    if((lencode-len)<minn){//要求删减数量尽量少
                        minn=lencode-len;
                        pi=i;
                    }
                }
                else if(lencode<len&&(strstr(str[i].Ciphertext,code)==str[i].Ciphertext)){//如果待解编码是字典里某元素的子串,且匹配位置为字典某元素的字符,说明该字典元素可由待解编码增加字符得到
                    if((len-lencode)<minn){//要求增加数量尽量少
                        minn=len-lencode;
                        pi=i;
                    }
                }
            }
            if(pi==-1)//若模糊匹配也无任何结果,则输出字典序最小的元素并加问号
                cout<<str[0].Plaintext<<"?"<<endl;
            else//可匹配则输出,并且末尾加问号
                cout<<str[pi].Plaintext<<"?"<<endl;
        }
    }
    return 0;
}
int toMatch(int &p){
    int cont=0;
    for(int i=0;i<n;i++)
        if(!strcmp(code,str[i].Ciphertext)){
            if(!cont)//匹配时输出字典序靠前的,所以p指向第一个匹配的就OK
                p=i;
            cont++;
        }
    return cont;//返回精确匹配的个数
}