【[SDOI2008]Sandy的卡片】

\(mhr\)的暴力干翻了

这道题做法还是非常好想的

先做一遍差分,在每个串的某尾插入一个特殊字符,再将所有的串拼接在一起

现在的问题就转化为找到一个最长的公共子串使得其出现了\(n\)次,但是在一个串内出现多次出现只算一次

先考虑一下没有第二个限制的做法

那就是最简单的\(SA\)+二分了,就是扫一遍\(height\)数组,之后根据\(height\)进行分组使得一个组内所有的\(height\)都大于等于当前二分出来的\(mid\),之后一个组内有多少个就代表这个子串出现了多少次

现在有了限制,就是不能来自于同一个串

于是我们给每一个\(sa_i\)打一个标记,标记好其来自哪一个数组,之后对于一个组内出现的同一个串内的我们只需要算一次就够了

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define LL long long
#define maxn (1000*100+1000+5)*2
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define pt putchar(1)
inline int read()
{
	char c=getchar();int x=0;
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int a[maxn],f[maxn],c[maxn];
int tp[maxn],sa[maxn],rk[maxn],tax[maxn],het[maxn],b[maxn];
int n,m,K,r,t;
inline void qsort()
{
	for(re int i=0;i<=K;i++) tax[i]=0;
	for(re int i=1;i<=n;i++) tax[rk[i]]++;
	for(re int i=1;i<=K;i++) tax[i]+=tax[i-1];
	for(re int i=n;i;--i) sa[tax[rk[tp[i]]]--]=tp[i];
}
inline int check(int x)
{
	int num=0,now=1;
	if(f[sa[1]]) num++,b[f[sa[1]]]++;
	for(re int i=2;i<=n;i++)
	{
		if(het[i]<x)
		{
			if(num>=m) return 1;
			for(re int j=now;j<i;j++) b[f[sa[j]]]=0;
			num=0,now=i;
			if(f[sa[i]]) num++,b[f[sa[i]]]++;
			continue;
		}
		if(!b[f[sa[i]]]&&f[sa[i]]) b[f[sa[i]]]++,num++;
	}
	return num>=m;
}
int main()
{
	m=read();
	for(re int i=1;i<=m;i++)
	{
		t=read();r=max(t,r);
		for(re int j=1;j<=t;j++) a[++n]=read(),f[n]=i;
		a[++n]=i+101,f[n]=0;
	}
	for(re int i=1;i<=n;i++) c[i]=a[i]-a[i-1];
	for(re int i=1;i<=n;i++) c[i]+=2001,K=max(c[i],K);
	c[1]=0;
	for(re int i=1;i<=n;i++) tp[i]=i,rk[i]=c[i];
	qsort();
	for(re int w=1,p=0;p<n;K=p,w<<=1)
	{
		p=0;
		for(re int i=1;i<=w;i++) tp[++p]=n-w+i;
		for(re int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
		qsort();
		for(re int i=1;i<=n;i++) std::swap(rk[i],tp[i]);
		rk[sa[1]]=p=1;
		for(re 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;
	}
	int k=0;
	for(re int i=1;i<=n;i++)
	{
		if(k) --k;
		int j=sa[rk[i]-1];
		while(c[i+k]==c[j+k]) ++k;
		het[rk[i]]=k;
	}
	int l=1,ans=0;
	while(l<=r)
	{
		int mid=l+r>>1;
		if(check(mid)) l=mid+1,ans=mid;
			else r=mid-1;
	}
	printf("%d\n",ans+1);
 	return 0;
}
posted @ 2019-01-01 19:32  asuldb  阅读(160)  评论(0编辑  收藏  举报