POJ3974 Manacher模板

该博客主要探讨了两种不同的回文串检测算法:基于字符串哈希的二分查找法和Manacher算法。二分查找法通过计算字符串的哈希值,检查子串是否为回文。Manacher算法利用对称性质,提高了寻找最长回文子串的效率。这两种方法在处理字符串时都有其独特的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二分+字符串HASH

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
string a;
int n;
typedef unsigned long long ll;
ll p[1000005];//p[i]表示27^i
ll f[1000005];//f[i]表示str[0~i]的hash函数值
ll rf[1000005];
bool chkp(ll mid)//回文串为奇数的情况例:aba
{
	for(int i=0;i<n;i++)
	{
	//遍历每一位hash查询str[i-mid~i]是否等于str[i+mid~i]
	//str[i-mid~i]=f[i]-f[i-mid-1]*p[(i)-(i-mid)+1]
	//str[i+mid~i]=rf[i]-rf[i+mid+1]*p[(i+mid)-(i)+1]
		ll l1=i-mid,r1=i,l2=i,r2=i+mid;
	//str[l1~r1]为回文串左边,str[l2~r2]为右边
		if(l1<0||r1>=n||l2<0||r2>=n)
		continue;
		ll fl1;
		ll rfr2;
		if(l1==0)//l1==0,f[l1-1]会越界,所以特判一下
		fl1=0;
		else
		fl1=f[l1-1];
		if(r2==n-1)//因为题目多组输入,所以rf[r2+1]可能不是0,所以特判一下
		rfr2=0;
		else
		rfr2=rf[r2+1];
		if(f[r1]-fl1*p[r1-l1+1]==rf[l2]-rfr2*p[r2-l2+1])//判断hash值是否相同,hash值相同则认为字符串相同
		return 1;
	}
	return 0;
}
bool chkq(ll mid)//回文串为偶数的情况例:abba
{
	for(int i=0;i<n;i++)
	{
	//遍历每一位hash查询str[i-mid~i-1]是否等于str[i+mid~i+1]
	//str[i-mid~i-1]=f[i-1]-f[i-mid-1]*p[(i-1)-(i-mid)+1]
	//str[i+mid~i+1]=rf[i+1]-rf[i+mid+1]*p[(i+mid)-(i+1)+1]
		ll l1=i-mid,r1=i-1,l2=i,r2=i+mid-1;
	//str[l1~r1]为回文串左边,str[l2~r2]为右边
		if(l1<0||l2<0||r1>=n||r2>=n)
		continue;
		ll fl1;
		ll rfr2;
		if(l1==0)//l1==0,f[l1-1]会越界,所以特判一下
		fl1=0;
		else
		fl1=f[l1-1];
		
		if(r2==n-1)//因为题目多组输入,所以rf[r2+1]可能不是0,所以特判一下
		rfr2=0;
		else
		rfr2=rf[r2+1];
		if(f[r1]-fl1*p[r1-l1+1]==rf[l2]-rfr2*p[r2-l2+1])//判断hash值是否相同,hash值相同则认为字符串相同
		return 1;
	}
	return 0;
}
int main()
{
	int c=0;
	while(cin>>a)
	{
		
		ll ansp=0,ansq=0;
		if(a=="END")
		break;
		n=a.size();
		p[0]=1;//p[i]表示27^i
		f[0]=a[0]-'a'+1;//f[i]表示str[0~i]的hash函数值
		for(int i=1;i<n;i++)
		{
			f[i]=f[i-1]*(ll)27+(ll)(a[i]-'a'+1);
			p[i]=p[i-1]*(ll)(27);
		} 
		rf[n]=0;//rf[i]表示str[n~i]的hash值,记住是从右到左的字符串的hash值
		for(int i=n-1;i>=0;i--)
		{
			rf[i]=rf[i+1]*27+(ll)(a[i]-'a'+1);
		}
		int l=1,r=n+1;
		while(l<r)//回文串为奇数的情况例:aba
		{
			int mid=l+r>>1;//mid二分回文串的一半长度,得到的答案需要*2+1得到回文串长度
			if(chkp(mid))
			{
				ansp=mid;//记录答案
				l=mid+1;
			}else
			r=mid;
		}
		l=0,r=n+1;
		while(l<r)//回文串为偶数的情况例:abba
		{
			int mid=l+r>>1;//mid二分回文串的一半长度,得到的答案需要*2得到回文串的长度
			if(chkq(mid))
			{
				ansq=mid;//记录答案
				l=mid+1;
			}else
			r=mid;
		}
		printf("Case %d: %lld\n",++c,max(ansp*2+1,ansq*2));
	}
return 0;
}

Manacher

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
const int SIZE=1e6+10;
int len[2*SIZE];//存储i位置上的回文串长度
char s[SIZE];
char str[SIZE*2];
int n=0;
int l=0;
void getstr()//将 abab 字符串变成 @#a#b#a#b#0
{
	int k=0;
	str[k++]='@';
	for(int i=0;i<n;i++)
	str[k++]='#',str[k++]=s[i];
	str[k++]='#';
	str[k++]='0';
	l=k;
}
int manacher()//跑manacher
{
	int mx=0;//当前已经判断的回文串中右边界最大的右边界位置
	int id=0;//当前已经判断的回文串中右边界最大的中心位置
	int ans=0;//记录回文串最大长度
	for(int i=1;i<l;i++)
	{
		if(i<mx)//假设j为i关于id对称,i<mx时,len[i]>=min(mx-i,len[j]),j=2*id-i
		len[i]=min(mx-i,len[2*id-i]);//这里需要注意如果len[i]=mx-i那么这个回文串中没有包括mx这个位置
		else
		len[i]=1;
		while(str[i+len[i]]==str[i-len[i]])len[i]++;//向左右拓展看看是否符合回文串
		//str[i+len[i]]不属于i为中点回文串中
		if(i+len[i]>mx)//更新mx,id和ans
		{
			mx=i+len[i];
			id=i;
			ans=max(ans,len[i]);
		}
	}
	return ans-1;
}
int main()
{
	int c=0;
	int ans=0;
	while(scanf("%s",s)&&s[0]!='E')
	{
		n=strlen(s);
		getstr();
		printf("Case %d: %d\n",++c,manacher());
	}
	
return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值