Programming Challenges 习题 2.8.4

PC/UVa:110204/843

Crypt Kicker

没想到这道题居然只能穷举。

对于密文的每一个单词,尝试和字典中的每一个单词匹配,如果可以添加没有冲突的映射关系,就继续尝试下一个可以匹配的单词。

如果在添加新的映射关系时,解密表mapDec或者加密表mapEnc中出现冲突,就应该把本单词新添加的映射关系去掉,然后就是要注意回溯法要保持递归之前和递归之后状态一致就行了。这道题WA了两次,是因为把vbNew放在了for循环外边,并且在恢复状态的时候没有把vbNew恢复,所以就错了,但是根据uDebug上的结果是测不出来的。

假如把abc解密为bcd,但是c解密为d时出现了冲突,在没有恢复vbNew的情况下,ab仍为新添加的映射关系;当再次把abc解密为efg时,此时如果a解密为e时发生了冲突,在恢复状态时,因为b也为新添加的,所以会从解密表中删除b(但是此时没有添加,mapDec删除也不会出错),但是在之前可能已经判断f加密为别的字符了,此时中mapEnc删除f的加密关系就会出错了。

#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include <set>
#include <vector>

using namespace std;

void decrypt(const set<string> &sstrCipher,
	set<string>::iterator sstriterCurr,
	map<string, bool> &mstrbDic,
	map<char, char>& mccDec, map<char, char>& mccEnc,
	map<char, char>& mccTmp, bool &bSolved)
{
	if (sstriterCurr == sstrCipher.end()){
		//找到了映射
		mccDec = mccTmp;
		bSolved = true;
		return;
	}
	vector<bool> vbNew(sstriterCurr->size(), false);//新添加的映射关系
	for (auto iter = mstrbDic.begin(); iter != mstrbDic.end(); iter++)
	{
		if (bSolved) return;
		//对于字典中每一个没有被映射过的单词
		if (!iter->second && iter->first.size() == sstriterCurr->size()){
			//判断是否存在重复映射
			bool bConflict = false;
			for (size_t i = 0; i < sstriterCurr->size(); i++)
			{
				//解密重复
				if (mccTmp.find(sstriterCurr->at(i)) != mccTmp.end()){
					if ((mccTmp[sstriterCurr->at(i)] != iter->first.at(i))){
						bConflict = true;
						break;
					}
				}
				//加密重复
				else if (mccEnc.find(iter->first.at(i)) != mccEnc.end()){
					if ((mccEnc[iter->first.at(i)] != sstriterCurr->at(i))){
						bConflict = true;
						break;
					}
				}
				else{
					mccTmp[sstriterCurr->at(i)] = iter->first.at(i);
					mccEnc[iter->first.at(i)] = sstriterCurr->at(i);
					vbNew[i] = true;
				}
			}
			if (bConflict){
				for (size_t i = 0; i < sstriterCurr->size(); i++)
				{
					if (vbNew[i]){
						mccTmp.erase(sstriterCurr->at(i));
						mccEnc.erase(iter->first.at(i));
						vbNew[i] = false;
					}
				}
				continue;
			}
			//继续映射下一个单词
			iter->second = true;
			sstriterCurr++;
			decrypt(sstrCipher, sstriterCurr, mstrbDic, mccDec, mccEnc, mccTmp, bSolved);
			sstriterCurr--;
			iter->second = false;
			for (size_t i = 0; i < sstriterCurr->size(); i++)
			{
				if (vbNew[i]){
					mccTmp.erase(sstriterCurr->at(i));
					mccEnc.erase(iter->first.at(i));
					vbNew[i] = false;
				}
			}
		}
	}
}

int main()
{
	int size = 0;
	cin >> size;
	string strWord, strCipher;
	map<string, bool> mstrbDic;
	for (int i = 0; i < size; i++)
	{
		cin >> strWord;
		mstrbDic[strWord] = false;
	}
	cin.get();
	while (getline(cin, strCipher)){
		set<string> sstrCipher;
		map<char, char> mccDec, mccEnc, mccTmp;
		istringstream iss(strCipher);
		while (iss >> strWord){
			sstrCipher.insert(strWord);
		}
		bool bSolved = false;
		decrypt(sstrCipher, sstrCipher.begin(), mstrbDic, mccDec, mccEnc, mccTmp, bSolved);
		for (auto c : strCipher)
		{
			if (c == ' ') cout << ' ';
			else {
				if (bSolved) cout << mccDec[c];
				else cout << '*';
			}
		}
		cout << endl;
	}
	return 0;
}
/*
6
and
dick
jane
puff
spot
yertle
bjvg xsb hxsn xsb qymm xsb rqat xsb pnetfn
xxxx yyy zzzz www yyyy aaa bbbb ccc dddddd
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值