P2463 [SDOI2008]Sandy的卡片

写一种\(O(nm)\)的做法,也就是\(O(\sum 串长)\)的。

先通过差分转化,把每个数变成这个数与上一个数的差,第一个数去掉,答案就是最长公共子串+1

按照套路把所有串拼起来,中间加一个分隔符号,然后用DC3求出SA以及height

(DC3我也不会,蒯的TJJ的板子,因为一般用倍增SA足够了)

对每个后缀可以知道它在原来的第几个串,那么求出SA以后,答案就是一段区间\([l,r]\),即排名为\([l,r]\)的这些后缀,而且必须覆盖原来的所有串,长度就是这些后缀的LCP,就是一段height的最小值

枚举左端点,那么最小的右端点一定不小于上一个的最小右端点,所以直接维护右端点以及这一段区间包括原来每一个串的数量就好了

最小值可以直接单调队列维护

#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
typedef unsigned long long ulint;
namespace input
{
    const int bufl = 1<<14;
    char buf[bufl],*s=buf,*t=buf;
    inline int fetch()
    {
        if(s==t){t=(s=buf)+fread(buf,1,bufl,stdin);if(s==t)return EOF;}
        return *s++;
    }
    inline int gi()
    {
        register int a=0,b=1,c=fetch();
        while(!isdigit(c))b^=c=='-',c=fetch();
        while(isdigit(c))a=a*10+c-48,c=fetch();
        return b?a:-a;
    }
}
using input::gi;
const int N = 202007 , N3 = N*3 , maxalpha = 99999;
int wa[N3],wb[N3],wv[N3],buc[N3];
inline void bsort(int *s,int *a,int *b,int n,int m)
{
    register int i;
    for(i=0;i<n;i++)wv[i]=s[a[i]];
    for(i=0;i<m;i++)buc[i]=0;
    for(i=0;i<n;i++)buc[wv[i]]++;
    for(i=1;i<m;i++)buc[i]+=buc[i-1];
    for(i=n-1;i>=0;i--)b[--buc[wv[i]]]=a[i];
}
inline int c0(int *s,int a,int b)
{
    return s[a]==s[b] && s[a+1]==s[b+1] && s[a+2]==s[b+2];
}
inline int c12(int k,int *s,int a,int b)
{
    if(k==2)return s[a]<s[b] || (s[a]==s[b] && c12(k-1,s,a+1,b+1));
    return s[a]<s[b] || (s[a]==s[b] && wv[a+1]<wv[b+1]);
}
#define f(x) ((x)/3+((x)%3==1?0:tb))
#define g(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
void dc3(int *s,int *sa,int n,int m)
{
    int i,j,*sn=s+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
    s[n]=s[n+1]=0;
    for(i=0;i<n;i++)if(i%3!=0)wa[tbc++]=i;
    bsort(s+2,wa,wb,tbc,m),bsort(s+1,wb,wa,tbc,m),bsort(s,wa,wb,tbc,m);
    for(sn[f(wb[0])]=0,p=1,i=1;i<tbc;i++)sn[f(wb[i])]=c0(s,wb[i-1],wb[i])?p-1:p++;
    if(p<tbc)dc3(sn,san,tbc,p);
    else for(i=0;i<tbc;i++)san[sn[i]]=i;
    for(i=0;i<tbc;i++)if(san[i]<tb)wb[ta++]=san[i]*3;
    if(n%3==1)wb[ta++]=n-1;
    bsort(s,wb,wa,ta,m);
    for(i=0;i<tbc;i++)wv[wb[i]=g(san[i])]=i;
    for(i=j=p=0;i<ta && j<tbc;p++)sa[p]=c12(wb[j]%3,s,wa[i],wb[j])?wa[i++]:wb[j++];
    for(;i<ta;p++)sa[p]=wa[i++];
    for(;j<tbc;p++)sa[p]=wb[j++];
}
#undef f
#undef g
void calheight(int *s,int *sa,int n,int *rk,int *ht)
{
    register int i,j,k=0;
    for(i=1;i<=n;++i)rk[sa[i]]=i;
    for(i=0;i<n;ht[rk[i++]]=k)for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k] && k<n;++k);
    return;
}
char s[N3];
int n,ss[N3],sa[N3],rk[N3],ht[N3];
int p[N3],S[N3];
int que[N3],hd,tl,cnt[N3];
int main()
{
    int NN=gi(),n=0,sep=0;
    if(NN==1)return printf("%d\n",gi()),0;
    while(NN--){
        int t=gi(),lst=gi();
        while(--t){
            int x=gi();
            S[n++]=lst-x+10000;
            lst=x;
        }
        S[n++]=++sep;
    }
    s[n]=0;
    dc3(S,sa,n+1,20000);
    calheight(S,sa,n,rk,ht);
    int k=1;
    for(int i=0;i<n;++i)if(S[i]>1000)p[i]=k;else ++k;//p[i]是后缀i属于原来的第几个串
    int CNT=0,r=k,Mx=0;//cnt是原来每个串的出现次数,CNT是总的
    hd=tl=0;
    --k;
    for(int i=k+1;i<=n;++i){
        while(r<=n&&CNT<k){
            if(cnt[p[sa[r]]]++==0)++CNT;
            if(r){
                while((hd^tl)&&ht[que[tl-1]]>ht[r])--tl;
                que[tl++]=r;
            }
            ++r;
        }
        if(CNT<k)break;
        while((hd^tl)&&que[hd]<=i)++hd;
        if(hd^tl)Mx=std::max(Mx,ht[que[hd]]);
        if(--cnt[p[sa[i]]]==0)--CNT;
    }
    printf("%d\n",Mx+1);
    return 0;
}

(大括号换行的是蒯的板子,不换行的也就是核心代码是我自己写的,DC3只是为了保证复杂度而已)

posted @ 2018-12-05 17:17  菜狗xzz  阅读(222)  评论(2编辑  收藏  举报