Poj1743 Musical Theme

42 篇文章 0 订阅
21 篇文章 0 订阅

题目传送门

题意:对一个序列的差分求出现至少两次且两次不相交的最长子串

也是SA和SAM都可以做,SA稍微麻烦

SA做法:二分+height分段rmq

SAM做法(太神了):对于每个节点的right集合,求出其中的最大值和最小值

那么合法的长度一定为min(mx[x],r[x]-l[x])(l和r分别是最大值和最小值)

#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 40005
using namespace std;
int s[N][180],mx[N],sz[N],f[N],a[N];
int n,m,lst=1,cnt=1,v[N],w[N],l[N],r[N];
inline int extend(int c){
	int p=lst,np=lst=++cnt,q,nq;
	mx[np]=mx[p]+1; sz[np]=1; l[np]=r[np]=mx[np];
	for(;p&&!s[p][c];p=f[p]) s[p][c]=np;
	if(!p) return f[np]=1;
	q=s[p][c];
	if(mx[q]==mx[p]+1) f[np]=q;
	else{
		nq=++cnt;
		mx[nq]=mx[p]+1;
		f[nq]=f[q]; f[q]=f[np]=nq;
		memcpy(s[nq],s[q],180<<2);
		for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;
	}
}
int _18520(){
	scanf("%d",&n); int ans=0; lst=cnt=1;
	if(!n) return 0;
	memset(l,127,sizeof(l));
	memset(r,0,sizeof(r));
	memset(v,0,sizeof(v));
	memset(mx,0,sizeof(mx));
	memset(f,0,sizeof(f));
	memset(s,0,sizeof(s));
	for(int i=1;i<=n;++i) scanf("%d",a+i);
	for(int i=1;i<n;++i) a[i]=a[i+1]-a[i]+88; --n;
	for(int i=1;i<=n;++i) extend(a[i]);
	for(int i=1;i<=cnt;++i) ++v[mx[i]];
	for(int i=1;i<=n;++i) v[i]+=v[i-1];
	for(int i=cnt;i;--i) w[v[mx[i]]--]=i;
	for(int p,i=cnt;i;--i){
		p=w[i]; sz[f[p]]+=sz[p];
		l[f[p]]=min(l[f[p]],l[p]);
		r[f[p]]=max(r[f[p]],r[p]);
	}
	for(int i=cnt;i;--i) ans=max(ans,min(mx[i],r[i]-l[i]));
	if(ans<4) puts("0"); else printf("%d\n",++ans); return 1;
}
int main(){ while(_18520()); }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值