kuangbin专题十八后缀数组总结

A - Musical Theme
这题关键点就在于如何用代码定义转调。经过分析后,我们可以用序列的每一个点与它前面的点作差,得到一个新序列,那么这个新序列中最大公共前缀就是答案。
同时,因为不能有公共部分,我们在求新序列的最长公共前缀时需要保证两个点的下标的距离大于长度加1,因为我们求出来是以差为值的序列的最长公共前缀,那么对应到原序列中,实际上应该包括求出来的下标-1的那个点,所以为了保证不能重叠,两个点的距离应该大于长度加1.

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
using namespace std;
#define rank rk
const int maxn=20005;
int ch[maxn];
int cntA[maxn],cntB[maxn],A[maxn],B[maxn],tsa[maxn],rank[maxn],SA[maxn],height[maxn];
int N;
void get_SA()
{
    for(int i=0; i<=200; i++)cntA[i]=0;
    for(int i=1; i<=N; i++)cntA[ch[i]]++;
    for(int i=1; i<=200; i++)cntA[i]+=cntA[i-1];
    for(int i=N; i>=1; i--)SA[cntA[ch[i]]--]=i;
    rank[SA[1]]=1;
    for(int i=2; i<=N; i++)
    {
        rank[SA[i]]=rank[SA[i-1]];
        if(ch[SA[i]]!=ch[SA[i-1]])
            rank[SA[i]]++;
    }
    for(int step=1;rank[SA[N]]<N;step<<=1)
    {
        for(int i=1;i<=N;i++)cntA[i]=cntB[i]=0;
        for(int i=1;i<=N;i++)
        {
            cntA[A[i]=rank[i]]++;
            cntB[B[i]=(i+step<=N)?rank[i+step]:0]++;
        }
        for(int i=1;i<=N;i++)cntA[i]+=cntA[i-1],cntB[i]+=cntB[i-1];
        for(int i=N;i>=1;i--)tsa[cntB[B[i]]--]=i;
        for(int i=N;i>=1;i--)SA[cntA[A[tsa[i]]]--]=tsa[i];
        rank[SA[1]]=1;
        for(int i=2;i<=N;i++)
        {
            rank[SA[i]]=rank[SA[i-1]];
            if(A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]])
                rank[SA[i]]++;
        }
    }
}
void get_Height()
{
    int i,j,k=0;
    for(i=1;i<=N;i++)
    {
        if(k)k--;
        j=SA[rank[i]-1];
        while(ch[i+k]==ch[j+k])k++;
        height[rank[i]]=k;
    }
}
int Begin[maxn],cnt;
int minid[maxn],maxid[maxn];
void pre_init(int num)
{
    for(int i=0;i<maxn;i++)minid[i]=N+1,maxid[i]=0;
    cnt=0;
    for(int i=1;i<=N;i++)
    {
        if(height[i]<num)
            Begin[++cnt]=i;
    }
    Begin[++cnt]=N+1;
    for(int i=1;i<cnt;i++)
    {
        for(int j=Begin[i];j<Begin[i+1];j++)
        {
            int theid=SA[j];
            //if(theid==1)continue;
            minid[i]=min(minid[i],theid);
            maxid[i]=max(maxid[i],theid);
        }
    }
}

bool check(int num)
{
    for(int i=1;i<=cnt;i++)
    {
        int themin=minid[i];
        int themax=maxid[i];
        if(themax-themin>=num+1)
            return 1;
    }
    return 0;
}

int main()
{
    while(~scanf("%d",&N)&&N)
    {
        for(int i=1; i<=N; i++)
            scanf("%d",&ch[i]);
        for(int i=N;i>=1;i--)
            ch[i]-=ch[i-1];
        for(int i=1;i<=N;i++)
            ch[i]+=90;
        //cout<<endl;
        get_SA();
        get_Height();

        int l=4,r=N/2;
        int mid,ans=-1;
        while(l<=r)
        {
            mid=l+r>>1;
            pre_init(mid);
            if(check(mid))
            {
                ans=mid;
                l=mid+1;
            }
            else
                r=mid-1;
        }
        printf("%d\n",ans+1);
    }
    return 0;
}

C - Distinct Substrings
找出所有不同的子串。
对于每个点来说,设它的排名为i,下标为idx,串长为len。它能提供的子串为len-idx+1(串下标从1到len)。而它与前面重复的串有height[i]个,所以减掉这个即可。

#include<bits/stdc++.h>
using namespace std;
#define rank rk
const int maxn=1005;
char ch[maxn];
int cntA[maxn],cntB[maxn],A[maxn],B[maxn],tsa[maxn],rank[maxn],SA[maxn],height[maxn];
int n;
void get_SA()
{
    for(int i=0;i<=300;i++)cntA[i]=0;
    for(int i=1;i<=n;i++)cntA[ch[i]]++;
    for(int i=1;i<=300;i++)cntA[i]+=cntA[i-1];
    for(int i=n;i>=1;i--)SA[cntA[ch[i]]--]=i;
    rank[SA[1]]=1;
    for(int i=2;i<=n;i++)
    {
        rank[SA[i]]=rank[SA[i-1]];
        if(ch[SA[i]]!=ch[SA[i-1]])
            rank[SA[i]]++;
    }
    for(int step=1;rank[SA[n]]<n;step<<=1)
    {
        for(int i=0;i<=n;i++)cntA[i]=0,cntB[i]=0;
        for(int i=1;i<=n;i++)
        {
            cntA[A[i]=rank[i]]++;
            cntB[B[i]=(i+step<=n)?rank[i+step]:0]++;
        }
        for(int i=1;i<=n;i++)cntA[i]+=cntA[i-1],cntB[i]+=cntB[i-1];
        for(int i=n;i>=1;i--)tsa[cntB[B[i]]--]=i;
        for(int i=n;i>=1;i--)SA[cntA[A[tsa[i]]]--]=tsa[i];
        rank[SA[1]]=1;
        for(int i=2;i<=n;i++)
        {
            rank[SA[i]]=rank[SA[i-1]];
            if(A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]])
                rank[SA[i]]++;
        }
    }
}

void get_Height()
{
    int i,j,k=0;
    for(i=1;i<=n;i++)
    {
        if(k)k--;
        j=SA[rank[i]-1];
        while(ch[i+k]==ch[j+k])k++;
        height[rank[i]]=k;
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",ch+1);
        n=strlen(ch+1);
        get_SA();
        get_Height();
        int ans=n-SA[1]+1;
        for(int i=2;i<=n;i++)
        {
            ans+=(n-SA[i]+1-height[i]);
        }
        cout<<ans<<endl;
    }
}

E - Power Strings
求最小循环节。
之前用kmp做的,有个结论就是最小循环节等于len-next[n],那么循环次数判断一下即可。
如何用后缀数组做呢?
做法比较简单,穷举字符串 S 的长度 k,然后判断是否满足。判断的时候,
先看字符串 L 的长度能否被 k 整除,再看 suffix(1)和 suffix(k+1)的最长公共
前缀是否等于 n-k。在询问最长公共前缀的时候,suffix(1)是固定的,所以 RMQ
问题没有必要做所有的预处理,只需求出 height 数组中的每一个数到
height[rank[1]]之间的最小值即可。整个做法的时间复杂度为 O(n)。
F - Repeats
求重复次数最多的重复子串。
我们可以枚举重复的长度L,如果重复长度达到了L的话,那么下标为1,1+L,1+2*L….中连续的两个就是一样的。所以我们在枚举下标,判断当前下标和下标+L的最长公共前缀,实际上我们还可以往前走,所以我们再把串倒过来,再求一次最长公共前缀,那么两次求的长度总和为len,k=len/L+1.

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
#define rank rk
const int maxn=50005;
int ch[2][maxn];
int cntA[2][maxn],cntB[2][maxn],A[2][maxn],B[2][maxn],SA[2][maxn],tsa[2][maxn],rank[2][maxn],Height[2][maxn];
int n;
void get_SA(int idx)
{
    int i;
    for(i=0;i<=2;i++)cntA[idx][i]=0;
    for(i=1;i<=n;i++)cntA[idx][ch[idx][i]]++;
    for(i=1;i<=2;i++)cntA[idx][i]+=cntA[idx][i-1];
    for(i=n;i>=1;i--)SA[idx][cntA[idx][ch[idx][i]]--]=i;
    rank[idx][SA[idx][1]]=1;
    for(i=2;i<=n;i++)
    {
        rank[idx][SA[idx][i]]=rank[idx][SA[idx][i-1]];
        if(ch[idx][SA[idx][i]]!=ch[idx][SA[idx][i-1]])
            rank[idx][SA[idx][i]]++;
    }
    for(int step=1;rank[idx][SA[idx][n]]<n;step<<=1)
    {
        int i;
        for(i=1;i<=n;i++)cntA[idx][i]=0,cntB[idx][i]=0;
        for(i=1;i<=n;i++)
        {
            cntA[idx][A[idx][i]=rank[idx][i]]++;
            cntB[idx][B[idx][i]=(i+step<=n)?rank[idx][i+step]:0]++;
        }
        for(i=1;i<=n;i++)
            cntA[idx][i]+=cntA[idx][i-1],cntB[idx][i]+=cntB[idx][i-1];
        for(i=n;i>=1;i--)
            tsa[idx][cntB[idx][B[idx][i]]--]=i;
        for(i=n;i>=1;i--)
            SA[idx][cntA[idx][A[idx][tsa[idx][i]]]--]=tsa[idx][i];
        rank[idx][SA[idx][1]]=1;
        for(i=2;i<=n;i++)
        {
            rank[idx][SA[idx][i]]=rank[idx][SA[idx][i-1]];
            if(A[idx][SA[idx][i]]!=A[idx][SA[idx][i-1]]||B[idx][SA[idx][i]]!=B[idx][SA[idx][i-1]])
                rank[idx][SA[idx][i]]++;
        }
    }
}

void get_Height(int idx)
{
    int i,j,k=0;
    for(i=1;i<=n;i++)
    {
        if(k)k--;
        j=SA[idx][rank[idx][i]-1];
        while(ch[idx][i+k]==ch[idx][j+k])k++;
        Height[idx][rank[idx][i]]=k;
    }
}
int themin[2][maxn][20];
void RMQ(int idx)
{
    for(int i=1;i<=n;i++)
        themin[idx][i][0]=Height[idx][i];
    for(int j=1;j<20;j++)
        for(int i=1;i<=n;i++)
            if(i+(1<<j)-1<=n)
            {
                themin[idx][i][j]=min(themin[idx][i][j-1],themin[idx][i+(1<<j-1)][j-1]);
            }
}
int qmin(int i,int j,int idx)
{
    int k=log2(j-i+1);
    return min(themin[idx][i][k],themin[idx][j-(1<<k)+1][k]);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(ch,-1,sizeof(ch));
        //int n;
        scanf("%d",&n);
        char s[3];
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s);
            if(s[0]=='a')ch[0][i]=0;
            else ch[0][i]=1;
        }
        for(int i=n;i>=1;i--)
            ch[1][n-i+1]=ch[0][i];

        /*for(int i=1;i<=n;i++)
            cout<<ch[0][i]<<' ';
        cout<<endl;
        for(int i=1;i<=n;i++)
            cout<<ch[1][i]<<' ';
        cout<<endl;
        */
        get_SA(0);
        get_Height(0);
        RMQ(0);
        get_SA(1);
        get_Height(1);
        RMQ(1);
        int ans=0;
        for(int L=1;L<=n;L++)
        {
            for(int Left=1;Left+L<=n;Left+=L)
            {
                int rk1=rank[0][Left],rk2=rank[0][Left+L];
                int rk3=rank[1][n+1-Left],rk4=rank[1][n+1-(Left+L)];
                int themin1=min(rk1,rk2),themax1=max(rk1,rk2);
                int themin2=min(rk3,rk4),themax2=max(rk3,rk4);
                int len=qmin(themin1+1,themax1,0);
                len+=max(0,qmin(themin2+1,themax2,1)-1);
                ans=max(ans,len/L+1);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

G - Maximum repetition substring
这题和上面的题一样,但是要输出字典序最小的字符串。我们还得用rmq预处理每个点的rank最小值。也就是2个后缀数组,3个rmq。但不知道哪里写丑了死活过不了,加了一些没必要计算的判断才过的。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const int maxn=100005;
#define rank rk
char ch[2][maxn];
int cntA[maxn],cntB[maxn],A[maxn],B[maxn],rank[2][maxn],tsa[maxn],SA[2][maxn],height[2][maxn];
int n;

void get_SA(char *ch,int *rank,int *SA)
{
    for (int i=0;i<=200;i++) cntA[i]=0;
    for (int i=1;i<=n;i++) cntA[ch[i]]++;
    for (int i=1;i<=200;i++) cntA[i]+=cntA[i-1];
    for (int i=n;i>=1;i--) SA[cntA[ch[i]]--] =i;
    rank[SA[1]]=1;
    for (int i=2;i<=n;i++){
        rank[SA[i]]=rank[SA[i-1]];
        if (ch[SA[i]]!=ch[SA[i-1]]) rank[SA[i]]++;
    }
    for (int step = 1;rank[SA[n]]<n;step<<=1){
        for (int i=0;i<=n;i++)cntA[i]=cntB[i]=0;
        for (int i=1;i<=n;i++){
            cntA[A[i]=rank[i]]++;
            cntB[B[i]=(i+step<=n)?rank[i+step]:0]++;
        }
        for (int i=1;i<=n;i++) cntA[i]+=cntA[i-1],cntB[i]+=cntB[i-1];
        for (int i=n;i>=1;i--) tsa[cntB[B[i]]--] =i;
        for (int i=n;i>=1;i--) SA[cntA[A[tsa[i]]]--] = tsa[i];
        rank[SA[1]]=1;
        for (int i=2;i<=n;i++){
            rank[SA[i]]=rank[SA[i-1]];
            if (A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]]) rank[SA[i]]++;
        }
    }
}

void get_Height(char *ch,int *rank,int *SA,int *height)
{
    int i,j,k=0;
    for(i=1;i<=n;i++)
    {
        if(k)k--;
        j=SA[rank[i]-1];
        while(ch[i+k]==ch[j+k])k++;
        height[rank[i]]=k;
    }
}
int minheight[2][maxn][20];
int minrank[maxn][20];
void RMQ(int (&minsum)[maxn][20],int *height)
{
    for(int i=1;i<=n;i++)
        minsum[i][0]=height[i];
    int k=log2(n);
    for(int j=1;j<=k;j++)
        for(int i=1;i<=n;i++)
            if(i+(1<<j)-1<=n)
            minsum[i][j]=min(minsum[i][j-1],minsum[i+(1<<(j-1))][j-1]);
}
int qmin(int a,int b,int (&minsum)[maxn][20])
{
    int k=log2(b-a+1);
    return min(minsum[a][k],minsum[b-(1<<k)+1][k]);
}

int queryLen(int rk1,int rk2,int (&minsum)[maxn][20])
{
    if(rk1>rk2)swap(rk1,rk2);
    return qmin(rk1+1,rk2,minsum);

}

int main()
{
    int cas=1;
    while(~scanf("%s",ch[0]+1)&&ch[0][1]!='#')
    {
        n=strlen(ch[0]+1);
        for(int i=n;i>=1;i--)
            ch[1][n-i+1]=ch[0][i];
        get_SA(ch[0],rank[0],SA[0]);
        get_Height(ch[0],rank[0],SA[0],height[0]);
        get_SA(ch[1],rank[1],SA[1]);
        get_Height(ch[1],rank[1],SA[1],height[1]);
        RMQ(minheight[0],height[0]);
        RMQ(minheight[1],height[1]);
        RMQ(minrank,rank[0]);
        int theminrank=n+1;
        int maxk=0;
        int thelen;
        for(int L=1;L<=n/2;L++)
        {
            int times=n/L;
            if(L!=1)times--;
            for(int j=0;j<times;j++)
            {
                int now_lc=j*L+1;
                if(ch[0][now_lc]!=ch[0][now_lc+L]&&L!=1)continue;
                int rightlen=queryLen(rank[0][now_lc],rank[0][now_lc+L],minheight[0]);
                int leftlen=queryLen(rank[1][n+1-now_lc],rank[1][n+1-now_lc-L],minheight[1]);
                int len=leftlen+rightlen;
                if(len)len--;
                int k=len/L+1;
                if(k==1&&L!=1)continue;
                int lidx,ridx;
                if(k==1)
                    lidx=ridx=now_lc;
                else
                {
                    lidx=now_lc-leftlen+1;
                    ridx=now_lc+L-k*L+rightlen;
                }
                int min_rank=qmin(lidx,ridx,minrank);
                if(k>maxk)
                {
                    maxk=k;
                    theminrank=min_rank;
                    thelen=L;
                }
                else if(k==maxk)
                {
                    if(min_rank<theminrank)
                    {
                        theminrank=min_rank;
                        thelen=L;
                    }
                }
            }
        }
        printf("Case %d: ",cas++);
        for(int i=1;i<=maxk;i++)
        {
            for(int j=SA[0][theminrank];j<SA[0][theminrank]+thelen;j++)
                printf("%c",ch[0][j]);
        }
        puts("");
    }
    return 0;
}

I - Common Substrings
求A串和B串所有连续相同子串的长度。
我们可以将两个串连接起来,中间加一个没有出现过的符号,后缀数组后,现在的问题就可以转换为,设在A的下标为i 在B的下标为j,找到height[rank[i]]到height[rank[j]]的最小值,即为他们的公共的前缀,如果枚举i和j肯定超时。
这时候我们可以用一个单调栈,来计算一个下标为A与它之前所有出现B的最长前缀。因为两个下标的最长前缀是两个下标排名之间的height的最小值,所以具有一个单调性,栈里面所有比它大的值都会弹出去,都会被它所替换,所以我们需要维护两个值,一是栈里的height的值,二是栈中所有的元素的个数。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
using namespace std;
#define rank rk
#define stack stk
const int maxn=100005;
char x[maxn],y[maxn];
char ch[maxn*2];
int cntA[maxn*2],cntB[maxn*2],A[maxn*2],B[maxn*2],tsa[maxn*2],rank[maxn*2],SA[maxn*2],Height[maxn*2];
int n;
typedef long long ll;
void get_SA()
{
    for (int i=0;i<=300;i++) cntA[i]=0;
    for (int i=1;i<=n;i++) cntA[ch[i]]++;
    for (int i=1;i<=300;i++) cntA[i]+=cntA[i-1];
    for (int i=n;i>=1;i--) SA[cntA[ch[i]]--] =i;
    rank[SA[1]]=1;
    for (int i=2;i<=n;i++){
        rank[SA[i]]=rank[SA[i-1]];
        if (ch[SA[i]]!=ch[SA[i-1]]) rank[SA[i]]++;
    }
    for (int step = 1;rank[SA[n]]<n;step<<=1){
        for (int i=0;i<=n;i++)cntA[i]=cntB[i]=0;
        for (int i=1;i<=n;i++){
            cntA[A[i]=rank[i]]++;
            cntB[B[i]=(i+step<=n)?rank[i+step]:0]++;
        }
        for (int i=1;i<=n;i++) cntA[i]+=cntA[i-1],cntB[i]+=cntB[i-1];
        for (int i=n;i>=1;i--) tsa[cntB[B[i]]--] =i;
        for (int i=n;i>=1;i--) SA[cntA[A[tsa[i]]]--] = tsa[i];
        rank[SA[1]]=1;
        for (int i=2;i<=n;i++){
            rank[SA[i]]=rank[SA[i-1]];
            if (A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]]) rank[SA[i]]++;
        }
    }

}

void get_Height()
{
    int i,j,k=0;
    for(i=1;i<=n;i++)
    {
        if(k)k--;
        j=SA[rank[i]-1];
        while(ch[i+k]==ch[j+k])k++;
        Height[rank[i]]=k;
    }
}
int stack[maxn*2],tot;
ll cnt[maxn*2],sum,ans;
int main()
{
    int K;
    while(~scanf("%d",&K)&&K)
    {
        memset(cnt,0,sizeof(cnt));
        ans=0;
        scanf("%s %s",x,y);
        int now=1;
        int len1=strlen(x);
        for(int i=0;i<len1;i++,now++)
            ch[now]=x[i];
        ch[now++]='$';
        int len2=strlen(y);
        for(int i=0;i<len2;i++,now++)
            ch[now]=y[i];
        n=now-1;
        get_SA();
        get_Height();
        int num;
        for(int i=1;i<=n;i++)
        {
            if(Height[i]<K)
            {
                sum=0;
                tot=0;
                continue;
            }
            num=0;
            while(tot&&stack[tot]>Height[i])
            {
                sum-=1LL*cnt[tot]*(stack[tot]-K+1);
                sum+=1LL*cnt[tot]*(Height[i]-K+1);
                num+=cnt[tot];
                tot--;
            }
            stack[++tot]=Height[i];
            if(SA[i-1]<=len1)
                cnt[tot]=num;
            else
                cnt[tot]=num+1,sum+=(Height[i]-K+1);
            if(SA[i]<=len1)
                ans+=sum;
        }
        for(int i=1;i<=n;i++)
        {
            if(Height[i]<K)
            {
                sum=0;
                tot=0;
                continue;
            }
            num=0;
            while(tot&&stack[tot]>Height[i])
            {
                sum-=1LL*cnt[tot]*(stack[tot]-K+1);
                sum+=1LL*cnt[tot]*(Height[i]-K+1);
                num+=cnt[tot];
                tot--;
            }
            stack[++tot]=Height[i];
            if(SA[i-1]>len1)
                cnt[tot]=num;
            else
                cnt[tot]=num+1,sum+=(Height[i]-K+1);
            if(SA[i]>len1)
                ans+=sum;
        }
        cout<<ans<<endl;
    }

    return 0;

}

N - Sequence
这题比较奇怪,数据范围没给,但毋庸置疑它是一道好题。由于数据范围没给,所以题目硬性要求我们将数据离散化。
再说如何做这个题,题目规定 A1>A2,....,An ,所以我们将这个串反过来,然后求后缀数组,排名最小的对应的下标就是第一段。第二段和第三段怎么求呢?
这个做法为什么我就想不到呢?
选完第一段后,将剩下的串复制到最后面去,然后在求一遍后缀数组,找到排名最小且下标在剩下的串那个范围内即可。
这样操作完之后,求出来的串就是需要的串,大家可以自己写一写理解一下。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
using namespace std;
#define rank rk
const int maxn=200005;
int a[maxn],b[maxn];
int ch[maxn];
int cntA[maxn],cntB[maxn],A[maxn],B[maxn],SA[maxn],tsa[maxn],rank[maxn],height[maxn];
int n;
void get_SA()
{
    for (int i=0;i<maxn;i++) cntA[i]=0;
    for (int i=1;i<=n;i++) cntA[ch[i]]++;
    for (int i=1;i<maxn;i++) cntA[i]+=cntA[i-1];
    for (int i=n;i>=1;i--) SA[cntA[ch[i]]--] =i;
    rank[SA[1]]=1;
    for (int i=2;i<=n;i++){
        rank[SA[i]]=rank[SA[i-1]];
        if (ch[SA[i]]!=ch[SA[i-1]]) rank[SA[i]]++;
    }
    for (int step = 1;rank[SA[n]]<n;step<<=1){
        for (int i=0;i<=n;i++)cntA[i]=cntB[i]=0;
        for (int i=1;i<=n;i++){
            cntA[A[i]=rank[i]]++;
            cntB[B[i]=(i+step<=n)?rank[i+step]:0]++;
        }
        for (int i=1;i<=n;i++) cntA[i]+=cntA[i-1],cntB[i]+=cntB[i-1];
        for (int i=n;i>=1;i--) tsa[cntB[B[i]]--] =i;
        for (int i=n;i>=1;i--) SA[cntA[A[tsa[i]]]--] = tsa[i];
        rank[SA[1]]=1;
        for (int i=2;i<=n;i++){
            rank[SA[i]]=rank[SA[i-1]];
            if (A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]]) rank[SA[i]]++;
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    sort(a+1,a+1+n);
    int len=unique(a+1,a+1+n)-a-1;
    for(int i=1;i<=n;i++)
    {
        int tmp=lower_bound(a+1,a+1+len,b[n-i+1])-a;
        ch[i]=tmp;
    }
    get_SA();
    int minrank=n+1;
    int minidx;
    for(int i=3;i<=n;i++)
    {
        if(minrank>rank[i])
        {
            minrank=rank[i];
            minidx=i;
        }
    }
    for(int i=minidx;i<=n;i++)
        printf("%d\n",a[ch[i]]);
    for(int i=minidx;i<=2*minidx-2;i++)
        ch[i]=ch[i-minidx+1];
    n=2*minidx-2;
    get_SA();
    for(int i=1;i<=2*minidx-2;i++)
    {
        if(SA[i]<=minidx-1&&SA[i]!=1)
        {
            int thisidx=SA[i];
            for(int j=thisidx;j<thisidx+minidx-1;j++)
                printf("%d\n",a[ch[j]]);
            break;
        }
    }
    return 0;
}

不知道为啥,刷题越来越没有手感了,每天都在写题却感觉自己越来越菜。。不管怎么样,还是得把kuangbin专题刷完。

猜你喜欢

转载自blog.csdn.net/qq_34921856/article/details/80041407