【ybtoj】【Hash】回文子串

题意

题目描述

如果一个字符串正着读和倒着读是一样的,则称它是回文的。

给定一个长度为 N 的字符串 S,求他的最长回文子串的长度是多少。

输入格式

输入将包含最多 30 个测试用例,每个测试用例占一行,以最多 106 个小写字符的形式给出。

一个以字符串 END(不包括引号)开头的行表示输入终止。

输出格式

对于输入中的每个测试用例,输出测试用例编号和最大回文子串的长度(参考样例格式)。每个输出占一行。

样例

样例输入

abcbabcbabcba
abacacbaaaab
END

样例输出

Case 1: 13 
Case 2: 6

题解

回文串还是经常遇见的一种题,所以第一时间想到:答案满足单调性所以二分长度,check函数里O(N)判断是否有长度为 mid 的回文串

有几个需要注意的点(我一开始没想到的):

  • 回文串要分奇回文和偶回文讨论
  • 存下反串和原串是对称的,写check的时候要注意一下

代码

回文子串
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f,N = 1e6+10,mod = 1e9+7,base = 233;
ll mi[N<<1],sum[N<<1];
int len,cnt;
char s[N<<1];
void init()
{
	mi[0]=1;
	for(int i=1;i<=len<<1;i++)
	{
		sum[i]=sum[i-1]*base+s[i]-'a'+1,sum[i]%=mod;
		mi[i]=mi[i-1]*base,mi[i]%=mod;
	}
}
inline ll Hash(int l,int r)
{
	return (sum[r]-sum[l-1]*mi[r-l+1]%mod+mod)%mod;
}
inline bool check1(int mid)
{
	for(int i=mid+1;i+mid<=len;i++)
		if(Hash(i-mid,i+mid)==Hash(len*2-(i+mid)+1,len*2-(i-mid)+1))
		{
			//printf("[%d,%d]==[%d,%d]
",i-mid,i+mid,len*2-(i+mid)+1,len*2-(i-mid)+1);
			return true;
		}
	return false;
}
inline bool check2(int mid)
{
	for(int i=mid;i+mid<=len;i++)
		if(Hash(i-mid+1,i+mid)==Hash(len*2-(i+mid)+1,len*2-(i-mid+1)+1))
		{
			//printf("[%d,%d]==[%d,%d]
",i-mid,i+mid,len*2-(i+mid)+1,len*2-(i-mid)+1);
			return true;
		}
	return false;
}
int main()
{
	while(1)
	{
		cnt++;
		scanf("%s",s+1);
		if(s[1]=='E'&&s[2]=='N'&&s[3]=='D') break;
		len=strlen(s+1);
		for(int i=1;i<=len;i++) s[i+len]=s[len-i+1];//在S末尾把反串存下来 
		init();
		int l=0,r=len,ans1=0,ans2=0;
		while(l<r)//奇回文 
		{
			int mid=(l+r+1)>>1;
			if(check1(mid)) l=mid;
			else r=mid-1;
		}
		ans1=(l<<1)+1;
		l=0,r=len;
		while(l<r)//偶回文 
		{
			int mid=(l+r+1)>>1;
			if(check2(mid)) l=mid;
			else r=mid-1;
		}
		ans2=(l<<1);
		printf("Case %d: %d
",cnt,max(ans1,ans2));
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/conprour/p/15232182.html