HDU 4300 Clairewd’s message
- 题意:给出一个密文匹配字符串(长度为26):拿样例来说,字符串按照位置和'a' + pos(0<= pos < 26)一一对应。然后再给一个目标字符串,总是密文在前明文在后,密文一定是完整的,让我们输出长度最短的密文和明文的原文。
扩展KMP做法
思路:
- 我们将原文 t 都看成密文,然后翻译成明文 tmp。然后以 t 为文本串,tmp为模式串,求 t 的后缀和 tmp的最大相同前缀的长度。
- 我们知道因为总是密文在前明文在后,密文一定是完整的,所以明文的长度最大是原文的一半长,而如果原文长度是奇数,那么明文的长度最大时len / 2。
- 我们枚举密文的长度 i = [len / 2 + len % 2, len),如果 i + extend[ i ] == len,那么说明 i 就是明文的首位置,结束。当然,为什么不需要继续跑完 i 的for循环呢,因为我们要输出长度最短的密文和明文的原文,那么肯定是密文越短,原文就越短咯~嘻嘻
也就是一个扩展KMP的模板吧,但是还是写了很久,开始就思路错了。www. 没有想到判断 i + extend[ i ] == len,而是找的最大的extend,怎么说呢,哪都不对吧。嗐!
扩展KMP AC CODE
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 1000000 + 100;
int Next[maxN], extend[maxN];
void GetNext(char *t, int len)
{
int front = 0, p = 0;
Next[0] = len;
for(int i = 1; i < len; i ++ )
{
if(i >= p || i + Next[i - front] >= p)
{
if(i >= p) p = i;
while(p < len && t[p] == t[p - i]) ++ p;
Next[i] = p - i;
front = i;
} else Next[i] = Next[i - front];
}
}
void GetExtend(char *s, int s_len, char *t, int t_len)
{
GetNext(t, t_len);
int front = 0, p = 0;
for(int i = 0; i < s_len; i ++ )
{
if(i >= p || i + Next[i - front] >= p)
{
if(i >= p) p = i;
while(p < s_len && p - i < t_len && s[p] == t[p - i]) ++ p;
extend[i] = p - i;
front = i;
} else extend[i] = Next[i - front];
}
}
char s[maxN], t[maxN], tmp[maxN];
int main()
{
int TAT; scanf("%d", &TAT);
while(TAT -- )
{
map<char, char>ming, mi;
scanf("%s%s", s, t);
int s_len = strlen(s), t_len = strlen(t);
for(int i = 0; i < s_len; i ++)
ming[s[i]] = 'a' + i;
for(int i = 0; i < t_len; i ++)
tmp[i] = ming[t[i]];
GetExtend(t, t_len, tmp, t_len);
int res = t_len;
for(int i = t_len / 2 + t_len % 2; i < t_len; i ++ )
{
if(extend[i] + i == t_len)
{
res = i;
break;
}
}
for(int i = 0; i < res; i ++ )
putchar(t[i]);
for(int i = 0; i < res; i ++ )
putchar(ming[t[i]]);
putchar('\n');
}
return 0;
}
顺便又写了一下哈希,又调了好久,20多分钟1A……太生了。前几天第一遍哈希做的时候简直做疯,也没至于那么疯哈哈。
哈希AC CODE
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 100000 + 7;
const ull base = 233;
ull Hash_ming[maxN], Hash_mi[maxN], p[maxN];
void pre()
{
p[0] = 1;
for(int i = 1; i < 100000; i ++ )
p[i] = p[i - 1] * base;
}
ull get_hash(int l, int r, ull *g)
{
return g[r] - g[l - 1] * p[r - l + 1];
}
char s[maxN], t[maxN];
map<char, char>ming;
int main()
{
pre();
int TAT; scanf("%d", &TAT);
while(TAT -- )
{
scanf("%s%s", s, t);
int s_len = strlen(s), t_len = strlen(t);
for(int i = 0; i < s_len; i ++ )
ming[s[i]] = 'a' + i;
//都翻译成明文
Hash_ming[0] = 0;
for(int i = 1; i <= t_len; i ++ )
Hash_ming[i] = Hash_ming[i - 1] * base + ming[t[i - 1]] - 'a' + 1;
//不翻译
Hash_mi[0] = 0;
for(int i = 1; i <= t_len; i ++ )
Hash_mi[i] = Hash_mi[i - 1] * base + t[i - 1] - 'a' + 1;
int res;
for(int i = t_len / 2 + t_len % 2; i <= t_len; i ++ )//枚举密文长度
{
//原来密文->明文 //原来就是明文
if(Hash_ming[t_len - i] == get_hash(i + 1, t_len, Hash_mi))
{
res = i;
break;
}
}
for(int i = 0; i < res; i ++ )
putchar(t[i]);
for(int i = 0; i < res; i ++ )
putchar(ming[t[i]]);
putchar('\n');
}
return 0;
}