BZOJ4698: Sdoi2008 Sandy的卡片
Description
Sandy和Sue的热衷于收集干脆面中的卡片。
然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积攒卡片兑换超炫的人物模型。
每一张卡片都由一些数字进行标记,第i张卡片的序列长度为Mi,要想兑换人物模型,首先必须要集够N张卡片。
对于这N张卡片,如果他们都有一个相同的子串长度为k,则可以兑换一个等级为k的人物模型。
相同的定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串。
Sandy的卡片数远远小于要求的N,于是Sue决定在Sandy的生日将自己的卡片送给Sandy。
在Sue的帮助下,Sandy终于集够了N张卡片,但是,Sandy并不清楚他可以兑换到哪个等级的人物模型。
现在,请你帮助Sandy和Sue,看看他们最高能够得到哪个等级的人物模型。
Input
第一行为一个数N,表示可以兑换人物模型最少需要的卡片数,即Sandy现在有的卡片数
第i+1行到第i+N行每行第一个数为第i张卡片序列的长度Mi,之后j+1到j+1+Mi个数,用空格分隔,分别表示序列中的第j个数
n<=1000,M<=1000,2<=Mi<=101
Output
一个数k,表示可以获得的最高等级。
Sample Input
2
2 1 2
3 4 5 9
2 1 2
3 4 5 9
Sample Output
2
题解Here!
题解Here!
后缀数组好题。
我们先把所有串连接在一起,用后缀数组求出$LCP$,然后二分长度,每次从头到尾扫一遍。
但是要注意连接的时候要加分割标记,这个只需要往两个串中间放一个大数就可以了。
根据$LCP$的某个定理:
$LCP(i,k)=min(LCP(j,j-1)),1<i<=j<=k<=n$
设$height[i]=LCP(i,i-1)$
只要有$n$个$>=len$的$height$且其首字母属于不同的串就可以了。
注:代码的变量名不是很好懂,见谅。。。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define MAXN 200010 #define MAXM 1010 #define MAX 999999999 using namespace std; int n,m,k,l,r; int val[MAXN],num[MAXN],stack[MAXN],id[MAXN],a[MAXM][MAXM]; int top=0,sa[MAXN],rk[MAXN],height[MAXN],tax[MAXN],tp[MAXN]; bool vis[MAXN]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } void radixsort(){ for(int i=0;i<=top;i++)tax[i]=0; for(int i=1;i<=n;i++)tax[rk[i]]++; for(int i=1;i<=top;i++)tax[i]+=tax[i-1]; for(int i=n;i>=1;i--)sa[tax[rk[tp[i]]]--]=tp[i]; } void suffixsort(){ top=m; for(int i=1;i<=n;i++){ rk[i]=val[i]; tp[i]=i; } radixsort(); for(int w=1,p=0;p<n;top=p,w<<=1){ p=0; for(int i=1;i<=w;i++)tp[++p]=n-w+i; for(int i=1;i<=n;i++)if(sa[i]>w)tp[++p]=sa[i]-w; radixsort(); swap(tp,rk); rk[sa[1]]=p=1; for(int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p; } } void getheight(){ for(int i=1,j,k=0;i<=n;i++){ if(k)k--; j=sa[rk[i]-1]; while(val[i+k]==val[j+k])k++; height[rk[i]]=k; } } bool check(int x){ while(top)vis[stack[top--]]=false; for(int i=1;i<=n;i++){ if(height[i]<x){ while(top)vis[stack[top--]]=false; } if(!vis[id[sa[i]]]){ vis[id[sa[i]]]=true; stack[++top]=id[sa[i]]; if(top==k)return true; } } return false; } void work(){ int mid,ans; top=0; while(l<=r){ mid=l+r>>1; if(check(mid)){ans=mid;l=mid+1;} else r=mid-1; } printf("%d\n",ans+1); } void init(){ int maxn=0,minn=MAX; k=n=read(); l=0;r=MAX; for(int i=1;i<=n;i++){ num[i]=read(); for(int j=1;j<=num[i];j++){ a[i][j]=read(); if(j!=1)maxn=max(maxn,a[i][j]-a[i][j-1]); } r=min(r,num[i]-1); } for(int i=1;i<=n;i++){ for(int j=2;j<=num[i];j++){ val[++top]=a[i][j]-a[i][j-1]; id[top]=i; minn=min(minn,val[top]); } val[++top]=++maxn; } for(int i=1;i<=top;i++){ val[i]-=minn-1; m=max(m,val[i]); } n=top; suffixsort(); getheight(); } int main(){ init(); work(); return 0; }