Ural 1297 Palindrome(后缀数组+最长回文子串)
https://vjudge.net/problem/URAL-1297
题意:
求最长回文子串。
思路:
先将整个字符串反过来写在原字符串后面,中间需要用特殊字符隔开,那么只需要某两个后缀的最长公共前缀。当然,这两个后缀不是让你随便选的,我们需要先确定回文串的中心(那么这儿就需要注意一下奇偶数的情况了,具体可以看一下代码),确定了中心之后,在后面的逆串中,我们也要找到这个中心点的位置,要求的是这两个后缀的公共前缀。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<vector> 6 #include<stack> 7 #include<queue> 8 #include<cmath> 9 #include<map> 10 #include<set> 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pll; 14 const int INF = 0x3f3f3f3f; 15 const int maxn=2000+5; 16 17 int n; 18 char s[maxn]; 19 int sa[maxn],t[maxn],t2[maxn],c[maxn]; 20 int Rank[maxn],height[maxn]; 21 int d[maxn][30]; 22 23 void build_sa(int m) 24 { 25 int *x=t,*y=t2; 26 //基数排序 27 for(int i=0;i<m;i++) c[i]=0; 28 for(int i=0;i<n;i++) c[x[i]=s[i]]++; 29 for(int i=1;i<m;i++) c[i]+=c[i-1]; 30 for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i; 31 for(int k=1;k<=n;k<<=1) 32 { 33 int p=0; 34 //直接利用sa数组排序第二关键字 35 for(int i=n-k;i<n;i++) y[p++]=i; 36 for(int i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k; 37 //基数排序第一关键字 38 for(int i=0;i<m;i++) c[i]=0; 39 for(int i=0;i<n;i++) c[x[y[i]]]++; 40 for(int i=1;i<m;i++) c[i]+=c[i-1]; 41 for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i]; 42 //根据sa和y计算新的x数组 43 swap(x,y); 44 p=1; 45 x[sa[0]]=0; 46 for(int i=1;i<n;i++) 47 x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++; 48 if(p>=n) 49 break; 50 m=p; //下次基数排序的最大值 51 } 52 } 53 54 void getHeight(int n) 55 { 56 int i,j,k=0; 57 for(i=1;i<=n;i++) Rank[sa[i]]=i; 58 for(i=0;i<n;i++) 59 { 60 if(k) k--; 61 int j=sa[Rank[i]-1]; 62 while(s[i+k]==s[j+k]) k++; 63 height[Rank[i]]=k; 64 } 65 } 66 67 void RMQ(int n) 68 { 69 for(int i=1;i<=n;i++) d[i-1][0]=height[i]; 70 for(int j=1;(1<<j)<=n;j++) 71 for(int i=0;i+(1<<j)-1<n;i++) 72 d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]); 73 } 74 75 int query(int L, int R) 76 { 77 int k=0; 78 while((1<<(k+1))<=R-L+1) k++; 79 return min(d[L][k],d[R-(1<<k)+1][k]); 80 } 81 82 int LCP(int a, int b) 83 { 84 int x=Rank[a],y=Rank[b]; 85 if(x>y) swap(x,y); 86 x--; y--; 87 if(y<0) return 0; 88 return query(x+1,y); 89 } 90 91 int main() 92 { 93 //freopen("in.txt","r",stdin); 94 while(~scanf("%s",s)) 95 { 96 int len = strlen(s); 97 s[len]='1'; 98 for(int i=0;i<len;i++) s[i+len+1]=s[len-1-i]; 99 s[2*len+1]='0'; //这儿后面的数要加的不一样,一开始两个都加了'0'... 100 n=2*len+2; 101 build_sa(128); 102 getHeight(n-1); 103 RMQ(n-1); 104 int ans=0, tmp, start; 105 for(int i=0;i<len;i++) 106 { 107 tmp=LCP(i,n-i-2); //长度为奇数的情况 108 if(2*tmp-1>ans) 109 { 110 ans=2*tmp-1; 111 start=i-tmp+1; 112 } 113 tmp=LCP(i,n-i-1); //长度为偶数的情况 114 if(2*tmp>ans) 115 { 116 ans=2*tmp; 117 start=i-tmp; 118 } 119 } 120 for(int i=start;i<start+ans;i++) 121 printf("%c",s[i]); 122 printf("\n"); 123 } 124 return 0; 125 }