扩展KMP
题目意思:
首先给出 26 个小写字母(这是密文),每个小写字母只出现一次。 第一个小写字母 对应的明文是 ‘a’, 第二个是 ‘b’。
依次类推。 题目的第2个样例的第1个字符串:qwertyuiopasdfghjklzxcvbnm, 那么 ‘q’ 的明文 是 ‘a’, ‘w’ 的明文是 ‘b’ …
第二个字符串, 由两部分组成,第一部分 是 全部的密文, 第二部分是部分的明文(部分明文, 全部明文,或者没有明文)。
问最短可能的字符串是 哪个。
本题要点:
1、以第二个样例为例子:
第2条字符串 “qwertabcde”(假设为 s1) , 将其全部看成是密文,全部转化为明文 “abcdekxvmc”(假设为 s2),
当 s1 的某个后缀 和 s2 的某个前缀 相等时候,说明 这部分相等的,可能是 原来字符串的部分明文。
然后,题目就转化为,求 s1 s1 的某个后缀 和 s2 的某个前缀相等 的最大长度。 但是,有个条件必须注意到,
s1 中, 密文的长度,至少是 s1 长度的一半。 所以,算 s1 的后缀的时候,必须从一半的位置开始算。
2、扩展KMP:
s1 为母串,s2 为子串,求出 s1 字符串extend 数组。从 超过 s1 一半的位置 k 开始扫描, 当 extend[k] 能到达
s1 串的末尾时候,就满足条件。 此时 extend[k] + k == len(s1 的长度)。
当所有扫描的点k 都不满足,说明,s1 串全部是 密文。
3、 输出源字符串的密文 和 明文。s1 部分输出密文, s2 部分输出明文。 通过吧某些位置的字符改为 ‘\0’, 然后
printf("%s") 输出。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MaxN = 100010;
char s[30], s1[MaxN], s2[MaxN];
int Next[MaxN], extend[MaxN];
char trans[256];
int T;
void init()
{
int len = strlen(s);
for(int i = 0; i < len; ++i)
{
trans[s[i] + 0] = i + 'a';
}
len = strlen(s1);
for(int i = 0; i < len; ++i)
{
s2[i] = trans[s1[i] + 0];
}
s2[len] = '\0';
}
//预处理next 数组
void getNext(char* str)
{
int i = 0, j, po, len = strlen(str);
Next[0] = len;
while(i + 1 < len && str[i] == str[i + 1]) // 计算 next[1]
++i;
Next[1] = i;
po = 1; //初始化 po位置
for(int i = 2; i < len; ++i)
{
if(Next[i - po] + i < Next[po] + po) //第一种情况
{
Next[i] = Next[i - po];
}else{
j = Next[po] + po - i;
if(j < 0)
j = 0; //如果i>po+next[po],则要从头开始匹配
while(i + j < len && str[j] == str[j + i])
++j;
Next[i] = j;
po = i;
}
}
}
//计算extend 数组
void exkmp(char* s1, char* s2) // s2 是子串,s1是母串
{
int i = 0, j, po, len = strlen(s1), l2 = strlen(s2);
getNext(s2);
while(i < l2 && i < len && s1[i] == s2[i])
++i;
extend[0] = i;
po = 0; //初始化po的位置
for(i = 1; i < len; ++i)
{
if(Next[i - po] + i < extend[po] + po)
{
extend[i] = Next[i - po];
}else{
j = extend[po] + po - i;
if(j < 0)
j = 0;
while(i + j < len && j < l2 && s1[j + i] == s2[j])
{
++j;
}
extend[i] = j;
po = i;
}
}
}
void solve()
{
init();
exkmp(s1, s2);
int len = strlen(s1);
bool flag = false;
int index = 0;
for(int i = (len + 1) / 2; i < len; ++i)
{
if(extend[i] + i == len)
{
flag = true;
index = i;
break;
}
}
if(flag)
{
s1[index] = '\0', s2[len - extend[index]] = '\0';
printf("%s%s\n", s1, s2);
}else{
printf("%s%s\n", s1, s2);
}
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%s%s", s, s1);
solve();
}
return 0;
}
/*
2
abcdefghijklmnopqrstuvwxyz
abcdab
qwertyuiopasdfghjklzxcvbnm
qwertabcde
*/
/*
abcdabcd
qwertabcde
*/