SuffixArray

后缀数组。
https://vjudge.net/contest/203023#overview
大约使用基数排序实现的 O(nlogn) 建立方法。
DC3比较麻烦就先不学了。
Hash+直接排序的方法在10行内完成。
复杂度为 O(nlog2n)
算是也可以用吧。

Problem C

求不同的子串个数。
根据SA的定义,可以比较简单地求出重复的子串个数,即Height值的和。

Code

struct SA{
    int Ht[M<<1],Rk[M<<1],sa[M];
    int C[M];
    void Solve(){
        int *x=Rk,*y=Ht;

        memset(Cnt,0,sizeof(Cnt));
        memset(Rk,0,sizeof(Rk));
        memset(Ht,0,sizeof(Ht));

        REP(i,0,n)Cnt[x[i]=C[i]]++;
        REP(i,1,M)Cnt[i]+=Cnt[i-1];
        REP(i,0,n)sa[--Cnt[x[i]]]=i;

        for(int k=1;k<=n;k<<=1){
            int p=0;
            REP(i,0,n)Cnt[x[sa[i]]]=i+1;
            REP(i,n-k,n)y[p++]=i;
            REP(i,0,n)if(sa[i]>=k)y[p++]=sa[i]-k;
            DREP(i,n-1,-1)sa[--Cnt[x[y[i]]]]=y[i];
            swap(x,y);
            x[sa[0]]=1;
            REP(i,1,n)x[sa[i]]=x[sa[i-1]]+
            (y[sa[i-1]]!=y[sa[i]] || y[sa[i-1]+k]!=y[sa[i]+k]);
        }

        int p=0,j;

        memset(Rk,0,sizeof(Rk));

        REP(i,0,n)Rk[sa[i]]=i;
        REP(i,0,n)if(j=Rk[i]){
            if(p)p--;j=sa[j-1];
            while(C[i+p]==C[j+p])p++;
            Ht[Rk[i]]=p;
        }
        Ht[0]=INF;
    }
    LL SumH(){
        LL Res=0;
        REP(i,1,n)Res+=Ht[i];
        return Res;
    }
}SA;

Problem D

最长回文子串的SA写法。
将原串改为str+‘$’+rev(str)。
分类讨论求某两个位置的LCP。
ST表求RMQ得LCP。

Code

struct SA{
    int Ht[M<<1],Rk[M<<1],sa[M],RMQ[M][K];
    char C[M];
    int LCP(int l,int r){
        if(l>r)swap(l,r);
        l++;int k=Nm[r-l+1];//Nm[i]=log2i
        return min(RMQ[l][k],RMQ[r-(1<1][k]);
    }
    void ReBuild(){
        REP(i,0,n)RMQ[i][0]=Ht[i];
        REP(k,1,K)REP(i,0,n)if((j=i+(1<1))1],RMQ[j][k-1]);
        }else break;
    }
    void Answer(){
        int Ans=0,Pos=-1;
        REP(i,0,m){
            int l=Query(Rk[i],Rk[n-i-1]);
            if(chkmax(Ans,(l<<1)-1))Pos=i-l+1;
        }
        REP(i,1,m)if(C[i]==C[i-1]){
            int l=Query(Rk[i],Rk[n-i]);
            if(chkmax(Ans,l<<1))Pos=i-l;
        }
        REP(i,Pos,Pos+Ans)putchar(C[i]);
        puts("");
    }
}SA;

Problem F

最大连续重复次数子串,字典序最小。
枚举长度,那么该串一定包含相邻该长度距离的两个字符。
分别求这两个字符向前向后的LCP,
即可得到重复次数,然后再通过对Rank值的RMQ求到字典序最小。
加了一些其他的优化。

Code

inline int Cmp(const int &a,const int &b){
    return SA1.Rk[a]int RKQ[M][K];
inline int Query(int l,int r){
    int k=Nm[r-l+1];
    return Cmp(RKQ[l][k],RKQ[r-(1<1][k]);
}
void Answer(){
    int j;
    REP(k,1,K) REP(i,0,n) if((j=i+(1<1))1],RKQ[j][k-1]);

    int Ans=1,Pos=SA1.sa[0],Len=1;
    REP(Lth,1,(n>>1)+1){
        for(int i=0;i+Lthint x,y=0;
            if(SA1.C[i]==SA1.C[i+Lth]){
                x=SA2.LCP(n-i-1,n-i-Lth-1),y=SA1.LCP(i,i+Lth);

                int Tmp=(x+y-1)/Lth+1;
                if(Tmp>=Ans){
                    int Ltp=Tmp*Lth,ps=Query(i-x+1,i+y+Lth-Ltp);

                    if(Ans==Tmp){if(SA1.Rk[ps]else Ans=Tmp,Pos=ps,Len=Ltp;
                }
            }
            i+=max(Lth,y);
        }
    }

    printf("Case %d: ",++Case);
    REP(i,Pos,Pos+Len)putchar(SA1.C[i]);
    puts("");
}

Problem G

最长公共子串。
即分属不同子串的相邻两个串的最大Height值。

Code

struct SA{
    void Answer(){
        int Ans=0;
        REP(i,1,n)if(Ans1]printf("%d\n",Ans);
    }
}SA;

int main(){
    scanf("%s",SA.C);
    SA.C[m=strlen(SA.C)]='$';
    scanf("%s",SA.C+m+1);
    n=strlen(SA.C);
    SA.Solve();
    SA.Answer();
    return 0;
}

Problem L

求一段子串的不同子串的个数。
在询问时保证相邻两个串的字典序,求其LCP并减去。

Code

#define IN(x) (l<=x && x<=r)
    void Answer(){
        int l,r;
        scanf("%d%d",&l,&r);l--,r--;

        int Len=r-l+1,Ans=Len*(Len+1)>>1,Lt=-1;

        REP(i,0,n)if(IN(sa[i])){
            if(Lt==-1)Lt=i;
            else{
                int Tmp=LCP(Lt,i);
                int la=r-sa[Lt]+1,lb=r-sa[i]+1;
                Ans-=min(min(la,lb),Tmp);
                if(laif(!(--Len))break;
            }
        }
        printf("%d\n",Ans);
    }

Problem N

max(LCP(Rk[i],Rk[j]))(j<i)
不会写就暴力。
二分答案,然后二分当前区间,求Rk最小值判断是否成立。

int Tmp;
    bool Check(int Len,int i){
        if(!Len)return 1;
        int a,Lt,Rt,l,r;
        a=Lt=Rt=Rk[i];

        l=0,r=a-1;
        while(l<=r){
            int Mid=l+r>>1;
            if(Query(RMQ,Mid+1,a)>=Len)Lt=Mid,r=Mid-1;
            else l=Mid+1;
        }

        l=a+1,r=n; 
        while(l<=r){
            int Mid=l+r>>1;
            if(Query(RMQ,a+1,Mid)>=Len)Rt=Mid,l=Mid+1;
            else r=Mid-1;
        }

        Tmp=Query(MK,Lt,Rt);
        return Tmpfor(int i=0;iint l=0,r=n,Res=0;
            while(l<=r){
                int Mid=l+r>>1;
                if(Check(Mid,i))Res=Mid,l=Mid+1;
                else r=Mid-1;
            }

            if(!Res)printf("-1 %d\n",C[i]),i++;
            else Check(Res,i),printf("%d %d\n",Res,Tmp),i+=Res;
        }
    }

然后正常的写法应该是单调栈。
i<j&&Ht[i]Ht[j] 时,保留i显然更优。

你可能感兴趣的:(字符串上的杂七杂八)