bzoj4698: Sdoi2008 Sandy的卡片(二分答案+后缀数组)

传送门
听说数据水???
其实这题更水好吧
题意:
n n n个数字串的最长公共子串长度。
两个子串相同的定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串
思路:
假如没有那个定义,把所有字符串拼在一起建一个 S A SA SA,中间用特殊字符连接,然后二分答案+双指针 c h e c k check check就完了。 废话
现在考虑如何转化相等这个条件。
假设两个串为 a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak b 1 , b 2 , . . . , b k b_1,b_2,...,b_k b1,b2,...,bk
那么他们相等的条件等价于是 ∏ [ a i − a i − 1 = = b i − b i − 1 , i > 1 ] \prod[a_i-a_{i-1}==b_i-b_{i-1},i>1] [aiai1==bibi1,i>1]
这样我们用一个串的差分数组来代替它然后二分答案即可,注意处理每个串的第一个数字。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int N=1005,M=2e5+5;
int n,s[M],m,len,t[M],up=1e9,pos[M],a[M];
namespace SA{
	int m,sa[M],sa2[M],rk[M],ht[M];
	inline void Sort(){
		static int cnt[M];
		for(ri i=1;i<=m;++i)cnt[i]=0;
		for(ri i=1;i<=n;++i)++cnt[rk[i]];
		for(ri i=1;i<=m;++i)cnt[i]+=cnt[i-1];
		for(ri i=n;i;--i)sa[cnt[rk[sa2[i]]]--]=sa2[i];
	}
	inline void getSA(){
		for(ri i=1;i<=n;++i)rk[i]=s[i],sa2[i]=i;
		m=7000;
		Sort();
		for(ri w=1,p=0;m^n;w<<=1,p=0){
			for(ri i=n-w+1;i<=n;++i)sa2[++p]=i;
			for(ri i=1;i<=n;++i)(sa[i]>w)&&(sa2[++p]=sa[i]-w);
			Sort(),swap(rk,sa2);
			rk[sa[p=1]]=1;
			for(ri i=2;i<=n;++i)rk[sa[i]]=(sa2[sa[i]]==sa2[sa[i-1]]&&sa2[sa[i]+w]==sa2[sa[i-1]+w])?p:++p;
			m=p;
		}
		for(ri i=1,j,k=0;i<=n;ht[rk[i++]]=k)for(k?--k:k,j=sa[rk[i]-1];s[i+k]==s[j+k];++k);
	}
	inline bool check(int len,int m){
		static int tot[N];
		for(ri ss,l=2,r;l<=n;l=r+1){
			while(l<n&&ht[l]<len)++l;
			if(ht[l]<len)break;
			r=l;
			while(r<n&&ht[r+1]>=len)++r;
			ss=0;
			for(ri i=l-1;i<=r;++i){
				if(!pos[sa[i]])continue;
				if(!tot[pos[sa[i]]])++ss;
				++tot[pos[sa[i]]];
			}
			for(ri i=l-1;i<=r;++i)if(pos[sa[i]])--tot[pos[sa[i]]];
			if(ss==m)return 1;
		}
		return 0;
	}
	inline void solve(int m){
		getSA();
		int l=0,r=up-1,res=0;
		while(l<=r){
			int mid=l+r>>1;
			if(check(mid,m))l=mid+1,res=mid;
			else r=mid-1;
		}
		cout<<res+1;
	}
}
int main(){
	freopen("lx.in","r",stdin);
	m=read();
	if(m==1)return cout<<read(),0;
	for(ri idx=4000,tt,i=1;i<=m;++i){
		s[++n]=++idx;
		up=min(up,tt=read());
		for(ri j=1;j<=tt;++j)a[j]=read();
		for(ri j=tt;j;--j)a[j]-=a[j-1]-2000;
		a[1]=++idx;
		for(ri j=1;j<=tt;++j)s[++n]=a[j],pos[n]=i;
	}
	SA::solve(m);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值