1250: 变为回文串
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 59 Solved: 18
[ Submit][ Status][ Web Board]
Description
我们现在给你一个字符串,我们规定只能在这个字符串的右边添加字符,问最少添加多少字符可以使这个字符串变为回文串?
回文串的定义见C题,不再赘述了。。。
Input
第一行一个正整数T<=10代表测试数据个数
每组测试数据给出一个字符串S,保证|S|<=1000000,且只包含小写字母
Output
对每组测试数据,输出最少需要添加的字符的个数
Sample Input
3ababaabad
Sample Output
103
HINT
Source
因此我们可以将字符串反转为str`。比较str[0 ~ i-1]和str`[len-i+1 ~ len]。用到Rabin-Karp。严格O(n)效率很高。
Rabin-Karp利用预处理出的字符串后缀的hash函数,在O(1)时间内求出任意子串的hash。
原理如下:
从右往左是高位到低位。
a1 a2 a3 a4 ... al al+1 ... am am+1 ... an
要求[l,m]的hash,减掉高位(靠右的[m+1,n])即可。
但是由于求hash时采用A[n] = A[n-1] * seed + bit[n]。因此,需要对减数需要乘上pow[m-l+1](类似于十进制的在低位补零)
hash使用unsigned long long可以自动对2^64取模(即自动抛掉高位),类似的,其他一些没有用unsigned的地方,应该直接用&7fffffff来代替绝对值,这样分布更均匀
小心因为可能会用到Hash2[len](定义它为0),因此每组数据需要把它写为0。
#include <cstdio>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <string>
#include <cstring>
typedef unsigned long long ull;
const int maxn = 1000010;
const double eps = 1e-6;
const int INF = 0x3f3f3f3f;
const ull seed = 31;
ull Hash1[maxn];
ull Hash2[maxn];
ull hash(char* str,ull* _Hash,int len)
{
ull _hash = 0;
for (int i=len-1;i>=0;i--)
{
_hash = _hash*seed + str[i]-'a'+1;
_Hash[i] = _hash;
}
return _hash;
}
ull pow[maxn];
ull getHash(int l)
{
return Hash2[0]-Hash2[l+1]*pow[l+1];
}
void clear(int len)
{
Hash2[len] = 0;
}
char str[maxn];
char str2[maxn];
int main()
{
int t;
scanf("%d",&t);
pow[0] = 1;
for (int i=1;i<maxn;i++)
{
pow[i] = pow[i-1] * seed;
}
while (t--)
{
scanf("%s",str);
int len = strlen(str);
clear(len);
for (int i=0;i<len;i++)
str2[i] = str[len-i-1];
hash(str,Hash1,len);
hash(str2,Hash2,len);
for (int i=len-1;i>=0;i--)
{
if (Hash1[len-i-1] == getHash(i))
{
printf("%d\n",len-i-1);
break;
}
}
}
return 0;
}