把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷3546】[POI2012] PRE-Prefixuffix(哈希)

题目链接

  • 给定一个长度为 \(n\) 的字符串,求不超过 \(\frac n2\) 的最大的 \(m\),使得长度为 \(m\) 的前缀与长度为 \(m\) 的后缀循环同构。
  • \(1\le n\le10^6\)

自然溢出双哈希不知道为什么被卡成了 \(90\) 分,换成取模哈希才过。。。(所以这篇博客主要是纪念哈希被卡)

循环同构转化

循环同构的经典转化:两个字符串 \(S_1,S_2\) 循环同构,等价于存在两个字符串 \(A,B\),满足 \(S_1=AB,S_2=BA\)

因此考虑去枚举 \(A\) 的长度,在 \(|A|\) 确定时求出最大的 \(|B|\)

发现这东西并不具备可二分性,无法无脑做。

\(f_i\)\(|A|=i\) 时的答案。考虑 \(|A|=i-1\) 时的答案 \(f_{i-1}\),意味着 \(s[i:i-1+f_{i-1}]\)\(s[n-(i-1)-f_{i-1}+1:n-(i-1)]\) 相同。

现在要以 \(i+1\) 为第一个串的左端点,\(n-i\) 为第二个串的右端点,由上面的条件可以知道肯定有 \(s[i+1:i+f_{i-1}-2]\)\(s[n-i-(f_{i-1}-2)+1:n-i]\) 相同,即 \(f_i\ge f_{i-1}-2\),也就是 \(f_i\le f_{i+1}+2\)

所以我们只要从大往小枚举 \(i\),初始令 \(f_i=f_{i+1}+2\),然后不断减小直至 \(f_i\) 合法即可。

代码:\(O(n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 1000000
using namespace std;
int n;char s[N+5];struct H
{
	#define P1 324682339
	#define P2 456789001
	int x,y;I H() {x=y=0;}I H(CI a) {x=y=a;}I H(CI a,CI b):x(a),y(b){}
	I H operator + (Cn H& o) Cn {return H((x+o.x)%P1,(y+o.y)%P2);}I H operator - (Cn H& o) Cn {return H((x-o.x+P1)%P1,(y-o.y+P2)%P2);}
	I H operator * (Cn H& o) Cn {return H(1LL*x*o.x%P1,1LL*y*o.y%P2);}I bool operator == (Cn H& o) Cn {return x==o.x&&y==o.y;}
}sd(23333),pw[N+5],h[N+5];map<H,int> G;
I bool Check(CI l1,CI r1,CI l2,CI r2) {return (h[r1]-h[l1-1])*pw[l2]==(h[r2]-h[l2-1])*pw[l1];}//哈希比较
int main()
{
	RI i;for(scanf("%d%s",&n,s+1),pw[0]=i=1;i<=n;++i) pw[i]=pw[i-1]*sd,h[i]=h[i-1]+pw[i]*s[i];
	RI t=0,f=0;for(i=n/2;i;--i) {f=min(f+2,n/2-i);W(f&&!Check(i+1,i+f,n-i-f+1,n-i)) --f;Check(1,i,n-i+1,n)&&(t=max(t,i+f));}//f[i]≤f[i+1]+2
	return printf("%d\n",t),0;
}
posted @ 2021-11-16 20:00  TheLostWeak  阅读(52)  评论(0编辑  收藏  举报