2010-08-03 299 views
4

虽然这看起来像Crypt Kicker Problem重复,事实并非如此。Crypt Kicker的更好解决方案?

我已经解决了这个问题,但我不都在一起满意我的解决方案。这个问题的说法是:

加密文本的共同但有不安全的方法是置换的英文字母。换句话说,字母表中的每个字母在文本中都被其他字母替换。为确保加密是可逆的,两个字母不会被同一个字母替换。

你的任务是解密文本的多个编码行,假设每个行使用不同的一组替代的,并且在解密文的所有单词都从已知字的字典。

输入

输入由含有一个整数n的线,后跟n小写字,每行一个,按字母顺序排列的。这n个单词组成可能出现在解密文本中的单词字典。字典后面有几行输入。每行都按上面所述加密。

有没有在字典中超过1000个字。没有单词超过16个字母。加密的行只包含小写字母和空格,长度不超过80个字符。

输出

解密每个行并打印到标准输出。如果有多种解决方案,任何人都会这样做。如果没有解决方案,请用星号替换字母表中的每个字母。

采样输入

迪克

粉扑

yertle

bjvg XSB hxsn XSB qymm XSB rqat XSB pnetfn

XXXX YYY ZZZZ WWW YYYY AAA BBBB CCC DDDDDD

样本输出

迪克和简并抽吸和点和yertle

**** *** **** *** **** *** **** *** ******


我蛮横逼迫了这个问题:我把字典分成了一个基于长度的集合。然后,我做了一个递归蛮力,我试着根据单词长度尝试每一个可能的替换,如果没有匹配,就回溯。它可行,但我对解决方案非常不满意。我可能只是在迷恋,但似乎应该有一个更优雅的方式来解决这个问题。我的代码如下:

#include<iostream> 
#include<algorithm> 
#include<vector> 
#include<sstream> 
#include<string> 
#include<map> 
#include<set> 
using namespace std; 
bool Find(vector<set<string > > &dict,vector<string> &line, map<char,char> &dec,int spot){ 
    //Check that the end of the line hasn't been reached 
    if(spot<line.size()){ 
    //Get the size of the current word 
    int sSize=line[spot].size(); 
    string cand; 
    cand.resize(sSize,'A'); 
    //Attempt to decode the current word 
    for(int i=0;i<sSize;i++){ 
     if(dec.find(line[spot][i])!=dec.end()) 
     cand[i]=dec[line[spot][i]]; 
    } 
    //Check all strings in the dictionary of the current length     
    for(set<string>::iterator it=dict[sSize].begin();it!=dict[sSize].end();it++){ 
     bool notMatch=false; 
     for(int i=0;i<sSize;i++) 
     //A is used to signify an undecoded character, this if says if the character was 
     // decoded and it does not equal to corresponding character in the word, it's not     a match 
     if(cand[i]!='A'&&cand[i]!=(*it)[i]) 
     notMatch=true; 
    if(notMatch) 
     continue; 
     for(int i=0;i<sSize;i++) 
     //if it is a feasible match, then add the learned characters to the decoder 
    if(cand[i]=='A') 
     dec.insert(pair<char,char> (line[spot][i],(*it)[i])); 
     //Keep decoding 
     if(Find(dict,line,dec,spot+1)) 
    return true; 
     //If decoding failed, then remove added characters 
     for(int i=0;i<sSize;i++) 
    if(cand[i]=='A') 
     dec.erase(line[spot][i]); 
    } 
    if(spot==0){ 
     //This means no solution was found, fill decoder with a map to astericks 
     string b="qwertyuiopasdfghjklzxcvbnm"; 
     for(int i=0;i<b.size();i++) 
    dec.insert(pair<char,char> (b[i],'*')); 
    } 
    return false; 
    } 
    return true; 
} 
int main(){ 
    int size; 
    cin >> size; 
    vector<set<string> > dict; 
    dict.resize(17); 
    string grab; 
    for(int i=0;i<size;i++){ 
    //Bucket dictionary 
    cin >> grab; 
    dict[grab.size()].insert(grab); 
    } 
    while(getline(cin,grab)){ 
    stringstream in(stringstream::in |stringstream::out); 
    in << grab; 
    vector<string> line; 
    while(in >> grab) 
     line.push_back(grab); 
    map<char,char> dec; 
    Find(dict,line,dec,0); 
    for(int i=0;i<line.size();i++){ 
     for(int j=0;j<line[i].size();j++) 
    cout << dec[line[i][j]]; 
     if(i!=line.size()-1) 
    cout << " "; 
     else 
    cout << endl; 
    } 
    } 
} 

另外,我不是特别感兴趣的解决方案,不会在c + +工作。仅仅因为这是我在编程比赛中使用的语言,所以我只能用它来解决这些问题。我也知道,有不少风格和次要效率的东西,我可以采取不同的做法,而不是太在乎我,我错过了一两次休息。主要我只是想知道是否有更简单的解决方案,或者如果我的实现过于复杂的事情。谢谢。

+1

招呼雅各,你让你的解决方案接受?我测试了你的,并没有为我的输入工作。 – Clash 2011-04-18 17:58:43

回答

4

我会通过比较单词中的字母模式来解决这个问题。首先,我会转换字典,像这样:

and -> 123 
dick -> 1234 
jane -> 1234 
puff -> 1233 
spot -> 1234 
yertle -> 123452 

这种特殊的字典里没有工作也很好,但总的想法是绘制出由字母组成的图案。例如单词“字母”映射到1233245,这是一个更好的例子,因为有多个e和t。

然后我会做同样的事情,以加密的文本:

bjvg xsb hxsn xsb qymm xsb rqat xsb pnetfn -> 1234 123 1234 123 1233 123 1234 123 123452 

我们可以做一个反向查找并确定第二个字是“和”,五是“噗”和第九是“ yertle”。 “dick”,“jane”和“spot”都有相同的模式,所以我们不能立即告诉他们,但使用从“and”,“puff”和“yertle”获得的信息可以填补其余部分。

+0

看来你可以用这种方法修剪搜索空间,通过缩小字典中每个单词的每个桶。一般而言,这将是有用的,但在问题的范围内会增加不必要的复杂性。一个好的想法,但。 – JSchlather 2010-08-03 20:29:20

+1

您还可以更智能地选择开始使用哪个单词。例如,在您的示例词典中,您有4个4个字母的单词,但只有1个3个字母的单词和1个6个字母的单词。您的密码文本以四个字母的单词开始,但它也包含三个和六个字母的单词。如果你从这些开始,你会更快地解密文本,因为你不需要回溯那些文本。这当然需要更多的信息(桶需要按大小排序,并且需要知道密文中每个单词的长度)。 – 2010-08-03 20:51:41

相关问题