URAL 1297:后缀数组求最长回文串

若如初见. 提交于 2020-03-26 01:40:49

求最长回文串

 

策略是枚举中心位置往两边拓展,这里要分长度为奇数、偶数的情况

n^2的复杂度无法接受,枚举中心后,这里用后缀数组来快速求回文长度

 

首先在字符串末尾插入一个未出现过的字符,目的是避免字符串翻转后拼接到末尾时的首部和原字符串末尾连接成新的后缀

比如说abac->abaccaba,这里的acca后缀是不存在的,所以应该变成abac#caba,阻隔开来

 

然后就是枚举中间点

首先说长度为奇数的情况

因为回文串至少长度为1,我们从下标1开始枚举至n-1

例如 0 1 2 3 4 5 6 7 8

   a b a c # c a b a  

  枚举到1时,我用2号位置起始的后缀和8号位置的后缀求最长公共前缀。因为8号位置的字符相当于是原串中的0号位置,相当于中心对称。以此类推

再来说偶数的情况

从0开始枚举至n-1。看看下面的例子理解一下为何从0开始

例如 0 1 2 3 4 5 6 7 8 9 10

   a b b a c # c a b b a  

  枚举到1时,我用2号位置的后缀和9号位置的后缀求最长公共前缀,9、10号对应原串1、0号,相当于轴对称。以此类推

 

不断维护长度最大值和起始位置,最后输出就行了

#include"cstdio"
#include"queue"
#include"cmath"
#include"stack"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"queue"
#include"map"
#include"set"
#include"vector"
#define ll long long
#define mems(a,b) memset(a,b,sizeof(a))
#define ls pos<<1
#define rs pos<<1|1

using namespace std;
const int MAXE = 500050;
const int MAXN = 2010;
const int INF = 0x3f3f3f3f;

int sa[MAXN];
int X[MAXN],Y[MAXN],radix[MAXN],trank[MAXN];
int RANK[MAXN];
char arr[MAXN];

void get_sa(char *r,int n,int m){
    int i,j,p,*Rank=X,*tsa=Y,*t;
    for (i=0;i<m;i++)  radix[i]=0;
    for (i=0;i<n;i++) radix[Rank[i]=r[i]]++;
    for (i=1;i<m;i++)  radix[i]+=radix[i-1];
    for (i=n-1;i>=0;i--)  sa[--radix[Rank[i]]]=i;
    for (j=1,p=1;p<n;j*=2,m=p)
    {
        for (p=0,i=n-j;i<n;i++) tsa[p++]=i;
        for (i=0;i<n;i++) if (sa[i]>=j) tsa[p++]=sa[i]-j;
        for (i=0;i<n;i++) trank[i]=Rank[tsa[i]];
        for (i=0;i<m;i++) radix[i]=0;
        for (i=0;i<n;i++) radix[trank[i]]++;
        for (i=1;i<m;i++) radix[i]+=radix[i-1];
        for (i=n-1;i>=0;i--) sa[--radix[trank[i]]]=tsa[i];
        for (t=Rank,Rank=tsa,tsa=t,p=1,Rank[sa[0]]=0,i=1;i<n;i++){
            if(tsa[sa[i-1]]==tsa[sa[i]]&&tsa[sa[i-1]+j]==tsa[sa[i]+j]) Rank[sa[i]]=p-1;
            else Rank[sa[i]]=p++;
        }
    }
    for(i = 0; i < n; i ++) RANK[sa[i]] = i;
    //for(i=0;i<n;i++) cout<<height[i]<<' ';cout<<endl;
    /*for(int i=0;i<n;i++){
        for(int j=sa[i];j<n;j++) cout<<r[j];
        cout<<endl;
    }*/

}
int height[MAXN];
void get_height(char *r,int n){
    int i,j=0,k=0;
    for(i = 0; i < n; height[RANK[i ++]] = k)
    for(k ? k -- : 0, j = sa[RANK[i]-1]; r[i+k] == r[j+k]; k ++);
}

int dp[MAXN][20];

void makermq(int n){
    mems(dp,INF);
    for(int i=1;i<=n;i++) dp[i][0]=height[i];
    for(int j=1;(1<<j)<=n;j++)
    for(int i=1;i+(1<<j)-1<n;i++) dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}

int query(int l,int r){
    if(l>r) swap(l,r);
    l++;
    int k=(int)(log((r-l+1)*1.0)/log(2.0));
    return min(dp[l][k],dp[r-(1<<k)+1][k]);
}

int main(){
    //freopen("in.txt","r",stdin);
    while(~scanf("%s",arr)){
        int n=strlen(arr);
        int len=n;
        arr[len]='#';
        for(int i=n-1;i>=0;i--) arr[++len]=arr[i];
        arr[++len]='0';
        get_sa(arr,len+1,300);
        get_height(arr,len);
        makermq(len+1);
        int ans=1,p=0;
        for(int i=1;i<n-1;i++){
            int t=query(RANK[i+1],RANK[2*n-i+1]);
            if(2*t+1>ans){
                ans=t*2+1;
                p=i-t;
            }
        }
        for(int i=0;i<n-1;i++){
            int t=query(RANK[i+1],RANK[2*n-i]);
            if(2*t>ans){
                ans=t*2;
                p=i-t+1;
            }
        }
        for(int i=0;i<ans;i++) cout<<arr[i+p];
        cout<<endl;
    }
    return 0;
}
View Code

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!