[Hash]变为回文串

1250: 变为回文串

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 59   Solved: 18
[ Submit][ Status][ Web Board]

Description

我们现在给你一个字符串,我们规定只能在这个字符串的右边添加字符,问最少添加多少字符可以使这个字符串变为回文串?
回文串的定义见C题,不再赘述了。。。

Input

第一行一个正整数T<=10代表测试数据个数
每组测试数据给出一个字符串S,保证|S|<=1000000,且只包含小写字母

Output

对每组测试数据,输出最少需要添加的字符的个数

Sample Input

3ababaabad

Sample Output

103

HINT

Source

wanghang


题目模型容易看出,是要求出最长的回文后缀,答案就是len减去它的长度。

因此我们可以将字符串反转为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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值