URAL 1297 求最长回文字符串

有种简单的方法,数组从左到右扫一遍,每次以当前的点为中心,只要左右相等就往左右走,这算出来的回文字符串是奇数长度的

还有偶数长度的回文字符串就是以当前扫到的点和它左边的点作为中心,然后往左右扫

这是O(n^2)的复杂度,这道题过还是没有问题的

 

这里我主要练习的是另外的利用后缀数组加RMQ算法来解决这个问题

大致思想跟上面一致

首先将字符串反转贴在原字符串末尾,将字符通过ASCII码转化为字符,之间用一个1分开,最后贴一个0

然后得到它的后缀数组以及height[]数组

那么找回文字符也是扫一遍,每次以当前点作为偶数情况和奇数情况的中心

然后找到后来倒置的字符串对应字符的位置,这样二者的前缀一个是往前走的,一个是往后走的,正好贴一块就是回文的

但是这个查询为加快速度,就要用RMQ算法,求得区间内height数组的最小值

 

  1 #include <cstdio>

  2 #include <cstring>

  3 #include <iostream>

  4 #include <cmath>

  5 using namespace std;

  6 #define MAXN 2010

  7 int sa[MAXN] , height[MAXN] , _rank[MAXN];

  8 int wa[MAXN] , wb[MAXN] , wsf[MAXN] , wv[MAXN] , r[MAXN];

  9 int dp[MAXN][15];

 10 char str[MAXN];

 11 

 12 int cmp(int *r , int a , int b , int l)

 13 {

 14     return r[a]==r[b] && r[a+l]==r[b+l];

 15 }

 16 

 17 void getSa(int *r , int *sa , int n , int m)

 18 {

 19     int i,j,p;

 20     int *x=wa , *y=wb , *t;

 21 

 22     for(i=0 ; i<m ; i++) wsf[i]=0;

 23     for(i=0 ; i<n ; i++) wsf[x[i]=r[i]]++;

 24     for(i=1 ; i<m ; i++) wsf[i]+=wsf[i-1];

 25     for(i=n-1 ; i>=0 ; i--) sa[--wsf[x[i]]]=i;

 26 

 27     p=1;

 28     for(j=1 ; p<n ; j*=2 , m=p)

 29     {

 30         for(p=0 , i=n-j ; i<n ; i++) y[p++]=i;

 31         for(i=0 ; i<n ; i++) if(sa[i]>=j) y[p++]=sa[i]-j;

 32 

 33         for(i=0 ; i<n ; i++) wv[i]=x[y[i]];

 34         for(i=0 ; i<m ; i++) wsf[i]=0;

 35         for(i=0 ; i<n ; i++) wsf[wv[i]]++;

 36         for(i=1 ; i<m ;  i++) wsf[i]+=wsf[i-1];

 37         for(i=n-1 ; i>=0 ; i--)sa[--wsf[wv[i]]]=y[i];

 38 

 39         t=x , x=y , y=t;

 40         x[sa[0]]=0;

 41         for(i=1,p=1;i<n;i++)

 42             x[sa[i]] = cmp(y , sa[i-1] , sa[i] , j)?p-1:p++;

 43     }

 44     return ;

 45 }

 46 

 47 void callHeight(int *r , int *sa , int n)

 48 {

 49     int i,j,k=0;

 50     for(i=1 ; i<=n ; i++) _rank[sa[i]]=i;

 51   //  for(i=0 ; i<n ; i++) cout<<"_rank: "<<_rank[i]<<endl;

 52     for(i=0 ; i<n ; height[_rank[i++]]=k)

 53         for(k?k--:0 , j=sa[_rank[i]-1]; r[i+k]==r[j+k] ; k++);

 54     return ;

 55 }

 56 

 57 

 58 void ST(int n)

 59 {

 60     //将height数组进行RMQ询问

 61     memset(dp , 0x3f , sizeof(dp));

 62     for(int i=1 ; i<=n ; i++) dp[i][0]=height[i];

 63     for(int j=1 ; (1<<(j-1))<n+1 ; j++)

 64         for(int i=1 ; i<=n ; i++)

 65             dp[i][j] = min(dp[i][j-1] , dp[i+(1<<(j-1))][j-1]);

 66 

 67     return;

 68 }

 69 

 70 int get_min(int s , int t)

 71 {

 72     int len = t-s+1;

 73     int l=floor(log(len*1.0)/log(2.0));

 74     return min(dp[s][l] , dp[t-(1<<l)+1][l]);

 75 }

 76 

 77 void solve(int n , int &left , int &right)

 78 {

 79     int ans = 0;

 80     //奇数情况

 81     for(int i=0 ; i<n ; i++){

 82         int j=2*n-i;

 83         int s=min(_rank[i] , _rank[j]);

 84         int t=max(_rank[i] , _rank[j]);

 85         int tmp = get_min(s+1 , t);

 86         if(ans<tmp*2-1){

 87             ans = tmp*2-1;

 88             left=i-tmp+1 , right=i+tmp-1;

 89         }

 90     }

 91     //偶数情况

 92     for(int i=0 ; i<n ; i++){

 93         int j = 2*n-i+1;

 94         int s=min(_rank[i] , _rank[j]);

 95         int t=max(_rank[i] , _rank[j]);

 96         int tmp = get_min(s+1 , t);

 97         if(ans<tmp*2){

 98             ans = tmp*2;

 99             left=i-tmp , right=i+tmp-1;

100         }

101     }

102     return;

103 }

104 

105 int main()

106 {

107   //  freopen("a.in" , "r" , stdin);

108     while(~scanf("%s" , str))

109     {

110         int len = strlen(str);

111         r[len]=1;

112         for(int i=0 ; i<len ; i++){

113             r[i] = (int)str[i];

114             r[2*len-i] = r[i];

115         }

116         r[2*len+1]=0;

117 

118         getSa(r , sa , 2*len+2 , 255);

119         callHeight(r , sa , 2*len+1);

120         //建立RMQ查询数组

121         ST(2*len+1);

122         int left=1 , right=1;

123         solve(len , left , right);

124         str[right+1]='\0';

125         printf("%s\n" , str+left);

126     }

127     return 0;

128 }

 

你可能感兴趣的:(字符串)