[SDOI2008]Sandy的卡片

[SDOI2008]Sandy的卡片

题目大意:

\(n(n\le1000)\)个长度为\(m_i(m_i\le100)\)的序列序列中每个元素\(a\)满足\(0\le a\le 1864\)。定义两个串\(A\)\(B\)是相似的当且仅当\(|A|=|B|\)\(A_i-B_i\)均相等。问所有序列的公共相似串最大长度。

思路:

对所有序列相邻数字作差,题目转化为最长公共子串问题。

显然,对于原来只有两个序列的情况,这是一个经典的最长公共子串问题。将所有的序列接在一起,相邻序列加入一个特殊点\(\infty\)。对合并以后的序列构建后缀数组,即可通过\(lcp\)数组求解。

对于有多个序列的情况,我们可以二分答案\(k\)。寻找\(lcp\)数组中是否有连续一段\(\ge k\)且包含了所有的序列。

时间复杂度\(\mathcal O(\sum m_i\log^2\sum m_i+\log(\min\{m_i\})nq)\)

源代码:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<climits>
#include<algorithm>
inline int getint() {
	register char ch;
	while(!isdigit(ch=getchar()));
	register int x=ch^'0';
	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
	return x;
}
const int N=1e5+5000,Q=1001;
int q,n,k,s[N],bel[N],sa[N],rank[N],lcp[N],tmp[N];
bool flag[Q];
inline bool cmp(const int &i,const int &j) {
	if(rank[i]!=rank[j]) return rank[i]<rank[j];
	const int ri=i+k<=n?rank[i+k]:-1;
	const int rj=j+k<=n?rank[j+k]:-1;
	return ri<rj;
}
inline void suffix_sort() {
	for(register int i=0;i<=n;i++) {
		sa[i]=i;
		rank[i]=s[i];
	}
	for(k=1;k<=n;k<<=1) {
		std::sort(&sa[0],&sa[n]+1,cmp);
		tmp[sa[0]]=0;
		for(register int i=1;i<=n;i++) {
			tmp[sa[i]]=tmp[sa[i-1]]+!!cmp(sa[i-1],sa[i]);
		}
		std::copy(&tmp[0],&tmp[n]+1,rank);
	}
}
inline void init_lcp() {
	for(register int i=0,h=0;i<n;i++) {
		if(h>0) h--;
		const int &j=sa[rank[i]-1];
		while(i+h<n&&j+h<n&&s[i+h]==s[j+h]) h++;
		lcp[rank[i]-1]=h;
	}
}
inline bool check(const int &k) {
	for(register int i=0;i<=n;i++) {
		if(lcp[i]<k) continue;
		int j=i;
		while(j<=n&&lcp[j]>=k) j++;
		j--;
		memset(flag,0,sizeof flag);
		for(register int k=i;k<=j+1;k++) {
			flag[bel[sa[k]]]=true;
		}
		i=j;
		for(j=1;j<=q&&flag[j];j++);
		if(j==q+1) return true;
	}
	return false;
}
int main() {
	q=getint();
	int l=0,r=INT_MAX;
	for(register int t=1;t<=q;t++) {
		const int m=getint();
		r=std::min(r,m-1);
		for(register int i=0;i<m;i++) {
			s[n+i]=getint();
		}
		for(register int i=1;i<m;i++) {
			s[n+i-1]-=s[n+i];
			bel[n+i-1]=t;
		}
		s[n+m-1]=INT_MAX;
		n+=m;
	}
	n--;
	suffix_sort();
	init_lcp();
	while(l<=r) {
		const int mid=(l+r)>>1;
		if(check(mid)) {
			l=mid+1;
		} else {
			r=mid-1;
		}
	}
	printf("%d\n",l);
	return 0;
}
posted @ 2018-06-12 17:03  skylee03  阅读(115)  评论(0编辑  收藏  举报