题目描述
Sandy和Sue的热衷于收集干脆面中的卡片。然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积
攒卡片兑换超炫的人物模型。每一张卡片都由一些数字进行标记,第i张卡片的序列长度为Mi,要想兑换人物模型
,首先必须要集够N张卡片,对于这N张卡片,如果他们都有一个相同的子串长度为k,则可以兑换一个等级为k的人
物模型。相同的定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串。Sandy的卡片数远
远小于要求的N,于是Sue决定在Sandy的生日将自己的卡片送给Sandy,在Sue的帮助下,Sandy终于集够了N张卡片
,但是,Sandy并不清楚他可以兑换到哪个等级的人物模型,现在,请你帮助Sandy和Sue,看看他们最高能够得到
哪个等级的人物模型。
广义后缀树
我们可以知道两个子串如果相同
那么剔除第一个元素和的差分序列应完全一致
因此可以把每个字符串变成剔除第一个元素后的差分序列。
模型转化为求所有串的最长公共子串长度+1。
可以对所有串建广义后缀树(不懂看我blog,大概就是trie上建sam的产物),接下来用线段树合并来得到一个点所代表的字符串集合在几个串中出现过。那我们在所有n个串里都出现过的状态找个max最大的作为答案。
#include<cstdio>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=110000+10,maxtot=2200000+10;
map<int,int> g[maxn*2];
int root[maxn*2],left[maxtot],right[maxtot],size[maxtot],fail[maxn*2],step[maxn*2];
int a[maxn*2],b[maxn*2];
int s[110];
int i,j,k,l,t,n,m,tot,top,last,mx,ans;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void insert(int &x,int l,int r,int a){
if (!x) x=++top;
if (l==r){
size[x]=1;
return;
}
int mid=(l+r)/2;
if (a<=mid) insert(left[x],l,mid,a);else insert(right[x],mid+1,r,a);
size[x]=size[left[x]]+size[right[x]];
}
int merge(int a,int b,int l,int r){
if (!a||!b) return a+b;
if (l==r){
size[a]=min(size[a]+size[b],1);
return a;
}
int mid=(l+r)/2;
left[a]=merge(left[a],left[b],l,mid);
right[a]=merge(right[a],right[b],mid+1,r);
size[a]=size[left[a]]+size[right[a]];
return a;
}
void add(int x){
int p=last;
if (g[p][x]){
int q=g[p][x];
if (step[q]==step[p]+1) last=q;
else{
int nq=++tot;
step[nq]=step[p]+1;
fail[nq]=fail[q];
fail[q]=nq;
g[nq]=g[q];
while (p&&g[p][x]==q){
g[p][x]=nq;
p=fail[p];
}
last=nq;
}
insert(root[last],1,n,i);
return;
}
int np=++tot;
step[np]=step[p]+1;
while (p&&g[p][x]==0){
g[p][x]=np;
p=fail[p];
}
if (!p) fail[np]=1;
else{
int q=g[p][x];
if (step[q]==step[p]+1) fail[np]=q;
else{
int nq=++tot;
fail[np]=nq;
step[nq]=step[p]+1;
fail[nq]=fail[q];
fail[q]=nq;
g[nq]=g[q];
while (p&&g[p][x]==q){
g[p][x]=nq;
p=fail[p];
}
}
}
last=np;
insert(root[last],1,n,i);
}
int main(){
tot=1;
n=read();
fo(i,1,n){
m=read();
fo(j,1,m) s[j]=read();
fd(j,m,2) s[j]-=s[j-1];
s[1]=0;
last=1;
fo(j,2,m) add(s[j]);
}
mx=0;
fo(i,1,tot)
if (mx<step[i]) mx=step[i];
fo(i,1,tot) b[step[i]]++;
fo(i,1,mx) b[i]+=b[i-1];
fd(i,tot,1) a[b[step[i]]--]=i;
ans=0;
fd(i,tot,2){
if (size[root[a[i]]]==n) ans=max(ans,step[a[i]]);
root[fail[a[i]]]=merge(root[fail[a[i]]],root[a[i]],1,n);
}
printf("%d\n",ans+1);
}