码迷,mamicode.com
首页 > 其他好文 > 详细

Ural1297 最长回文串

时间:2020-02-22 00:45:20      阅读:92      评论:0      收藏:0      [点我收藏+]

标签: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

  

Ural1297 最长回文串

标签:ace   公共前缀   快速   scan   接下来   lse   bre   work   就是   

原文地址:https://www.cnblogs.com/cutemush/p/12343508.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!