[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;
}