思路:kmp+next数组的应用
分析:
1 题目要求的是给定一个不完整的由n个字符构成的字符串并且字符串有密文和明文两部分组成,现在要求完整的字符串。
2 很明显密文的长度不确定,现在又要求最短的字符串长度。由于在完整的字符串中密文和明文的长度是一样的,那么输入的字符串中至少有(n+1)/2是密文,所以我们假设密文后面的都是明文,那么我们将这些明文转化为密文后求next数组就可以知道前缀和后缀的最大的匹配数,那么如果匹配数越大就说明总的串的长度就越小。
3 但是有个地方需要注意,输入的字符串长度为n,那么至少(n+1)/2为密文,那么明文最多为tmp = n-(n+1)/2,所以next[n]的最大值是不能超过tmp的,如果超过了那么就另next[len] = tmp即可,说明已经最大了。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAXN 100010
#define N 30
int t;
int next[MAXN];
char mp[N];
char pattern[MAXN];
/**求最大的匹配数*/
void getNext(){
int len = strlen(pattern);
next[0] = next[1] = 0;
for(int i = 1 ; i < len ; i++){
int j = next[i];
while(j && pattern[i] != pattern[j])
j = next[j];
next[i+1] = pattern[i] == pattern[j] ? j+1 : 0;
}
}
int main(){
char str[MAXN];
scanf("%d" , &t);
while(t--){
scanf("%s" , mp);
scanf("%s" , pattern);
int len = strlen(pattern);
/*求最大的匹配数*/
memcpy(str , pattern , sizeof(pattern));
for(int i = (len+1)/2 ; i < len ; i++){
int num = pattern[i]-'a';
pattern[i] = mp[num];
}
getNext();
/*当最大匹配数超过(len-(len+1)/2)的是就要直接把next[len] = len-(len+1)/2*/
if(next[len] > (len-(len+1)/2))
next[len] = len-(len+1)/2;
int num = len-next[len];
printf("%s" , str);
/*输出剩下的明文*/
int j = num-next[len];
int i = num-j;
/*把密文转化为明文*/
for(; i < num ; i++){
for(int j = 0 ; j < 26 ; j++){
if(str[i] == mp[j]){
printf("%c" , j+'a');
break;
}
}
}
printf("\n");
}
return 0;
}
利用kmp的扩展
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 100010
#define N 30
int t;
int next[MAXN];
int extend[MAXN];
char mp[N];
char text[MAXN];
char pattern[MAXN];
/*求扩展kmp的next*/
void getNext(){
int len = strlen(pattern);
int a = 0;
next[0] = len;
while(a < len-1 && pattern[a] == pattern[a+1])
a++;
next[1] = a;
a = 1;/*a重新赋值为1*/
for(int k = 2 ; k < len ; k++){
int p = a+next[a]-1;
int l = next[k-a];
if(k+l-1 >= p){
int j = max(p-k+1 , 0);
while(k+j < len && pattern[k+j] == pattern[j])
j++;
next[k] = j;
a = k;
}
else
next[k] = l;
}
}
/*求estend数组*/
void getExtend(){
int n = strlen(text);
int m = strlen(pattern);
int a = 0;
int minLen = min(n , m);
while(a < minLen && text[a] == pattern[a])
a++;
extend[0] = a;
a = 0;
for(int k = 1 ; k < n ; k++){
int p = a+extend[a]-1;
int l = next[k-a];
if(k-1+l >= p){
int j = max((p-k+1) , 0);
while(k+j < n && j < m && text[k+j] == pattern[j])
j++;
extend[k] = j;
a = k;
}
else
extend[k] = l;
}
}
int main(){
char str[MAXN];
int max;
scanf("%d" , &t);
while(t--){
scanf("%s" , mp);
scanf("%s" , pattern);
int len = strlen(pattern);
memcpy(str , pattern , sizeof(pattern));
/*求出模式串*/
int pos = 0;
memset(text , '\0' , sizeof(text));
for(int i = (len+1)/2 ; i < len ; i++){
int num = pattern[i]-'a';
text[pos++] = mp[num];
}
pattern[(len+1)/2] = '\0';
/*求next数组*/
getNext();
/*求extend数组*/
getExtend();
/*求最长的公共前缀数*/
int max = 0;
for(int i = 0 ; i < pos ; i++){
if(max < extend[i] && extend[i]+i == pos)
max = extend[i];
}
/*输出*/
printf("%s" , str);
int cir = len-max;/*求出密文的长度*/
int j = cir-max;
int i = cir-j;/*求出要输出的起始位置*/
for(; i < cir ; i++){
for(int j = 0 ; j < 26 ; j++){
if(str[i] == mp[j]){
printf("%c" , j+'a');
break;
}
}
}
printf("\n");
}
return 0;
}