Caesar Cipher凯撒密码

题目如下:(注意可能不按字典序排列)
在这里插入图片描述
这些入门算法题处境很尴尬,大佬轻松ac,像我这种小白查了半天资料不知道怎么搞,这样一来,我这种弱鸡网上就没得嫖 ,蒟蒻玩家表示初学很心累。
主要是kmp算法,如果不懂参考这篇文章:什么是kmp算法
先说下题意吧,给出字符A,明文w,和加密文s,在s中单词w只会出现一次(注意题目黑体字,英语不好我纳闷这题很久 ),输出位移密码即可。
当然先处理字符串,A中只包含a-z;A-Z;0-9;这些都是ascii码表中字符,凯撒密码按照一个shift值来排密码,此处不一定按字典序,比如三例D7a,有三种可能对应情况,位移0,1,2。
也就是说把w中的D7a字符换成D7a,7aD,aD7;(为什么要换w?因为我喜欢爱好 ,因为w不超过50000而text S大小500000)在s中查找w换掉任一项字符串即可,这样位移为1的时候,D7a换成7aD,只出现了一次,位移为2时,D7a换成a7D,在s中只出现一次。
怎么换呢?从A中去查对应位移后字符 ,这显然查到超时了,既然A中字符就26+26+10个,那直接建立一个62大小数组按顺序把a-z;A-Z;0-9替换即可,当然这些都是ascii表中数字,直接建立一个128大小的数组来换也不算耗空间,而且简单。
定义一个int型ascii[128]数组,比如D7a位移1位是7aD,这样‘D’会换成‘7’字符,(D的坐标加1就是7的位置)只需要在‘D’位置(字符’D’转成int型是68)换成‘7’字符大小即可。

for (int i = 0; i < la; i++) {
    
    
				ord1 = a[i];
				ord2 = a[(i + k) % la];//k是位移数,la是字符串A的长度
				ascii[ord1] = ord2 ;//ascii数组用来转换字符
			}

然后转换w字符串


			for (int i = 0; i < lw; i++) {
    
    //lw是w字符串长度
				nw[i] =ascii[w[i]];
			}

这样用nw和S进行kmp查找,查找到出现大于一次就返回重新把位移k++,如果等于一次就说明这个位移k就是密码。0次说明密码不对,k++;
ac代码(这个时间复杂度还是比较高O(n2),直接用的板子kmp返回字符串x在y中出现次数,因为太菜越改越多bug ,大佬可能做法更巧妙)

#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;
const int maxn = 1000005;
int nxt[maxn];
void getnext(char *s)
{
    
    
	int i = 0, j = -1, len = strlen(s);
	nxt[0] = -1;
	while (i < len)
	{
    
    
		if (j == -1 || s[i] == s[j])
		{
    
    
			if (s[++i] == s[++j])    nxt[i] = nxt[j];
			else    nxt[i] = j;
		}
		else    j = nxt[j];
	}
}

int kmp(char *x, char *y)
{
    
    
	getnext(x);
	int i = 0, j = 0, ans = 0, leny = strlen(y), lenx = strlen(x);
	while (i < leny)
	{
    
    
		if (j == -1 || x[j] == y[i])
		{
    
    
			i++, j++;
			if (j == lenx)
			{
    
    
				ans++;
				j = nxt[j];
			}
		}
		else    j = nxt[j];
	}
	return ans;/实际上不用返回次数,定义一个bool型在循环体内部判断ans>1就返回false减少查找
}


int main() {
    
    
	int n;
	char a[65];
	int aa[65];
	char w[50005], t[500005];
	char nw[50005];
	int ascii[128];
	int ord1, ord2;
	cin >> n;
	while (n--) {
    
    
		cin >> a >> w >> t;
		int la = strlen(a);
		int lw = strlen(w);
		int lt = strlen(t);
		int num = 0;//num统计解的个数
		for (int k = 0; k < la; k++) {
    
    //k表示位移,只能是0~A的长度-1;
			for (int i = 0; i < la; i++) {
    
    
				ord1 = a[i];
				ord2 = a[(i + k) % la];
				ascii[ord1] = ord2 ;
			}
			for (int i = 0; i < lw; i++) {
    
    
				nw[i] =ascii[w[i]];
			}
			nw[lw] = '\0';
			if (kmp(nw, t)==1) {
    
    
				aa[num++] = k;
			}
		}
		if (num == 0)cout << "no solution" << endl;
		else if (num == 1)cout << "unique: " << aa[0] << endl;
		else {
    
    
			printf("ambiguous:");
			for (int i = 0; i < num; i++) {
    
    
				printf(" %d", aa[i]);
			}
			printf("\n");
		}
	}


	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43305312/article/details/106954269