标签:ace 公共前缀 快速 scan 接下来 lse bre work 就是
考虑将原串反向后接在原串后面,中间用一个不在字符集里的字符隔开,那么我们可以对前半部分字符串的每个字符与后半部分字符串的对应字符求最长公共前缀,即为答案。考虑以第i个字符为回文串的中心/对称轴右侧字符(在前面一半字符串相当于向右走,在后面那个字符串中相当于向左走),则回文串分别包含奇数/偶数个字符。当回文串包含奇数个字符,第i个字符的对应字符是n-i+1,回文串长是lcp(i,n-i+1)*2-1,回文串开始位置是i-lcp+1;当回文串包含偶数个字符,第i个字符的对应字符是n-i+2,回文串长是lcp(i,n-2+1)*2,回文串开始位置是i-lcp。
接下来的问题就是快速求lcp(l,r),这就可以利用后缀数组和sparse-table算法了
zz:http://blog.sina.com.cn/s/blog_4a0c4e5d01019j35.html
001 #include<cstdio>
002 #include<cstring>
003 #include<algorithm>
004 using namespace std;
005 int f[4005][4005];
006 int height[4005];
007 int rank[4005];
008 int sa[4005];
009 char st[4005];
010 int h[4005];
011 int n,p,ans;
012 template <typename T>
013 void radix(int a[],int b[],T s[],int n,int m)
014 { int i;
015 for (i=0;i<=m;++i)
016 h[i]=0;
017 for (i=1;i<=n;++i)
018 ++h[s[a[i]]];
019 for (i=1;i<=m;++i)
020 h[i]+=h[i-1];
021 for (i=n;i>0;--i)
022 b[h[s[a[i]]]--]=a[i];
023 }
024 void init_sa()
025 { int i,j;
026 int a[4005],b[4005];
027 for (i=1;i<=n;++i)
028 rank[i]=i;
029 radix(rank,sa,st,n,256);
030 rank[sa[1]]=1;
031 for (i=2;i<=n;++i)
032 if (st[sa[i-1]]!=st[sa[i]])
033 rank[sa[i]]=rank[sa[i-1]]+1;
034 else rank[sa[i]]=rank[sa[i-1]];
035 for (i=1;i<=n;i*=2)
036 { for (j=1;j<=n;++j)
037 { a[j]=rank[j];
038 if (i+j<=n)
039 b[j]=rank[i+j];
040 else b[j]=0;
041 sa[j]=j;
042 }
043 radix(sa,rank,b,n,n);
044 radix(rank,sa,a,n,n);
045 rank[sa[1]]=1;
046 for (j=2;j<=n;++j)
047 if (a[sa[j-1]]!=a[sa[j]]||b[sa[j-1]]!=b[sa[j]])
048 rank[sa[j]]=rank[sa[j-1]]+1;
049 else rank[sa[j]]=rank[sa[j-1]];
050 if (rank[sa[n]]==n)
051 break ;
052 }
053 }
054 void calc_h()
055 { int i,p=0;
056 for (i=1;i<=n;++i)
057 { if (p>0)
058 --p;
059 if (rank[i]!=1)
060 while (st[i+p]==st[sa[rank[i]-1]+p])
061 ++p;
062 height[rank[i]]=p;
063 }
064 }
065 void init_rmq()
066 { int i,j;
067 for (i=1;i<=n;++i)
068 f[i][0]=height[i];
069 for (j=1;(1<<j)<=n;++j)
070 for (i=1;i+j-1<=n;++i)
071 f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
072 }
073 int rmq(int l,int r)
074 { int k=0;
075 if (l>r)
076 swap(l,r);
077 while ((1<<(k+1))<=r-l+1)
078 ++k;
079 return min(f[l][k],f[r-(1<<k)+1][k]);
080 }
081 int lcp(int l,int r)
082 { int a,b;
083 a=rank[l],b=rank[r];
084 if (a>b)
085 swap(a,b);
086 return rmq(a+1,b);
087 }
088 int work()
089 { int i,k;
090 p=ans=1;
091 for (i=1;i<=n/2;++i)
092 { k=lcp(i,n-i+1);
093 if (k*2-1>ans)
094 ans=k*2-1,p=i-k+1;
095 k=lcp(i,n-i+2);
096 if (k*2>ans)
097 ans=k*2,p=i-k;
098 }
099 return ans;
100 }
101 int main()
102 { int i,ans;
103 scanf("%s",st+1);
104 n=strlen(st+1);
105 st[n+1]=‘#‘;
106 for (i=n+2;i<=n*2+1;++i)
107 st[i]=st[n-(i-n)+2];
108 n=n*2+1;
109 init_sa();
110 calc_h();
111 init_rmq();
112 ans=work();
113 for (i=p;i<p+ans;++i)
114 printf("%c",st[i]);
115 printf("\n");
1
标签:ace 公共前缀 快速 scan 接下来 lse bre work 就是
原文地址:https://www.cnblogs.com/cutemush/p/12343508.html