• 字符串(马拉车算法,后缀数组,稀疏表):BZOJ 3676 [Apio2014]回文串


    Description

    考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出
    现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最
    大出现值。

    Input

    输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。

    Output


    输出一个整数,为逝查回文子串的最大出现值。

    Sample Input

    【样例输入l】
    abacaba

    【样例输入2]
    www

    Sample Output

    【样例输出l】
    7

    【样例输出2]
    4

    HINT



    一个串是回文的,当且仅当它从左到右读和从右到左读完全一样。

    在第一个样例中,回文子串有7个:a,b,c,aba,aca,bacab,abacaba,其中:

    ● a出现4次,其出现值为4:1:1=4

    ● b出现2次,其出现值为2:1:1=2

    ● c出现1次,其出现值为l:1:l=l

    ● aba出现2次,其出现值为2:1:3=6

    ● aca出现1次,其出现值为1=1:3=3

    ●bacab出现1次,其出现值为1:1:5=5

    ● abacaba出现1次,其出现值为1:1:7=7

    故最大回文子串出现值为7。

    【数据规模与评分】

    数据满足1≤字符串长度≤300000。

      这道题要求出每一个回文串,以及出现的次数。

      先用马拉车算法求出每个位置最长的回文子串,可以证明:从每个位置上取出最长的回文串去枚举一定能枚举出最优解。

      接着对于,每个位置的最长回文子串用SA与ST快速求出其出现的次数,然而这并非正解,回文自动机才是(话说APIO2014的时候还没有这玩意儿),那时这个算法应该是最优的了。

      1 #include <iostream>
      2 #include <cstring>
      3 #include <cstdio>
      4 using namespace std;
      5 const int maxn=300100;
      6 char s[maxn],S[maxn<<1];
      7 int maxl[maxn<<1];
      8 int pos[maxn<<1],maxL[maxn];
      9 int rank[maxn],Wa[maxn],Wb[maxn],Wv[maxn],Ws[maxn],sa[maxn],r[maxn];
     10 int lcp[maxn],len;
     11 int mm[maxn],Min[maxn][32];
     12 bool cmp(int *p,int a,int b,int l){
     13     return p[a]==p[b]&&p[a+l]==p[b+l];
     14 }
     15 
     16 void DA(int n,int m){
     17     int i,j,p,*x=Wa,*y=Wb,*t;
     18     for(i=0;i<m;i++)Ws[i]=0;
     19     for(i=0;i<n;i++)++Ws[x[i]=r[i]];
     20     for(i=1;i<m;i++)Ws[i]+=Ws[i-1];
     21     for(i=n-1;i>=0;i--)sa[--Ws[x[i]]]=i;
     22     
     23     for(j=1,p=1;p<n;j<<=1,m=p){
     24         for(p=0,i=n-j;i<n;i++)y[p++]=i;
     25         for(i=0;i<n;i++)
     26             if(sa[i]>=j)
     27                 y[p++]=sa[i]-j;
     28         
     29         for(i=0;i<m;i++)Ws[i]=0;
     30         for(i=0;i<n;i++)++Ws[Wv[i]=x[y[i]]];
     31         for(i=1;i<m;i++)Ws[i]+=Ws[i-1];
     32         for(i=n-1;i>=0;i--)sa[--Ws[Wv[i]]]=y[i];
     33         for(t=x,x=y,y=t,x[sa[0]]=0,i=1,p=1;i<n;i++)    
     34             x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
     35     }
     36 }
     37 
     38 void Lcp(int n){
     39     int i,j,k=0;
     40     for(i=1;i<=n;i++)rank[sa[i]]=i;
     41     for(i=0;i<n;lcp[rank[i++]]=k)
     42         for(k?--k:k,j=sa[rank[i]-1];r[j+k]==r[i+k];++k);
     43 }
     44 int Qmin(int l,int r){
     45     return min(Min[l][mm[r-l+1]],Min[r-(1<<mm[r-l+1])+1][mm[r-l+1]]);
     46 }
     47 
     48 int Query(int p,int l){
     49     int ret=1,lo,hi;
     50     hi=rank[p];lo=0;
     51     while(lo<=hi){
     52         int mid=(lo+hi)>>1;
     53         if(Qmin(mid,rank[p])>=l)hi=mid-1;
     54         else lo=mid+1;
     55     }
     56     ret+=rank[p]-lo+1;    
     57     
     58     hi=len;lo=rank[p]+1;
     59     while(lo<=hi){
     60         int mid=(lo+hi)>>1;
     61         if(Qmin(rank[p]+1,mid)>=l)lo=mid+1;
     62         else hi=mid-1;
     63     }
     64     return ret+hi-rank[p];
     65 }
     66 
     67 int main(){
     68     scanf("%s",s);
     69     len=strlen(s);
     70     int l=0;
     71     S[l++]='#';S[l++]='&';
     72     for(int i=0;i<len;i++){
     73         pos[l]=i;
     74         S[l++]=s[i];
     75         pos[l]=i+1;
     76         S[l++]='&';
     77     }
     78     S[l]=0;
     79     int mx=0,id;
     80     for(int i=1;i<l;i++){
     81         maxl[i]=mx>i?min(maxl[id*2-i],mx-i):1;
     82         while(S[i+maxl[i]]==S[i-maxl[i]])maxl[i]++;
     83         if(i+maxl[i]>mx){
     84             mx=i+maxl[i];
     85             id=i;
     86         }
     87     }
     88     
     89     for(int i=1;i<=l;i++)
     90         maxL[pos[i-maxl[i]+1]]=max(maxL[pos[i-maxl[i]+1]],maxl[i]-1);
     91     //maxL[i]原s字符串中i位置最长回文子串
     92     for(int i=0;i<len;i++)r[i]=s[i];
     93     DA(len+1,128);
     94     Lcp(len);
     95     
     96     mm[0]=-1;
     97     for(int i=1;i<=len;i++){
     98         mm[i]=(i&(i-1))?mm[i-1]:mm[i-1]+1;
     99         Min[i][0]=lcp[i];
    100     }
    101     
    102     for(int k=1;k<=mm[len];k++)
    103     for(int i=1;i+(1<<k)-1<=len;i++)
    104     Min[i][k]=min(Min[i][k-1],Min[i+(1<<(k-1))][k-1]);
    105     
    106     long long ans=0;
    107     for(int i=0;i<len;i++){
    108         ans=max(ans,1ll*maxL[i]*Query(i,maxL[i]));
    109     }
    110     printf("%lld
    ",ans);
    111     return 0;
    112 }

      然后还有一种方法,前面提过,回文自动机:

     1 #include <iostream>
     2 #include <cstdio>
     3 using namespace std;
     4 const int maxn=300010;
     5 int last,cnt,ch[maxn][26],fail[maxn],len[maxn],num[maxn];
     6 long long ans=0;
     7 char s[maxn];
     8 int find(int i,int x){
     9     while(s[i-len[x]-1]!=s[i])x=fail[x];
    10     return x;
    11 }
    12 int main(){
    13     scanf("%s",s);
    14     fail[0]=1;len[1]=-1;cnt=1;
    15     for(int i=0;s[i];i++){
    16         int j=find(i,last);
    17         if(ch[j][s[i]-'a'])
    18             last=ch[j][s[i]-'a'];
    19         else{
    20             len[last=++cnt]=len[j]+2;
    21             fail[last]=ch[find(i,fail[j])][s[i]-'a'];
    22             ch[j][s[i]-'a']=last;
    23         }    
    24         num[last]++;
    25     }
    26     for(int i=cnt;i>=0;i--)
    27         ans=max(ans,1ll*num[i]*len[i]),num[fail[i]]+=num[i];
    28     printf("%lld
    ",ans);
    29     return 0;    
    30 }

      超级短,超级简单,但还是有些地方要注意:

      14行的初始化不能乱改,改了挺麻烦的;21,22行是有顺序的,写反了会死循环。

    尽最大的努力,做最好的自己!
  • 相关阅读:
    火狐浏览器看哪些地方加nofollow
    2017.6.14-网站分析
    2017.6.11-目标关键词优化 三个方面内容
    2017-6-9长尾关键词优化
    2017.6.7seowhy学习笔记---seo知识总纲
    使用 WordPress 自定义字段功能为文章添加下载按钮
    xftp和xshell有什么区别
    决定网站排名的6个干货
    转载:如何在wordpress主题中添加设置页面
    win10+ubuntu双系统安装方案
  • 原文地址:https://www.cnblogs.com/TenderRun/p/5285144.html
Copyright © 2020-2023  润新知