hdu3948-不同回文串的个数后缀数组

Posted Konjak谷弱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu3948-不同回文串的个数后缀数组相关的知识,希望对你有一定的参考价值。

题意:求不同回文串的个数 n<=10^5

题解:

先按照manacher的构造方法改造一遍串,然后跑一遍manacher。

如ababa--> $#a#b#a#b#a#@

然后跑一遍后缀数组。

对于一个后缀sa[i]~cl(cl为字符串的总长),我们本来是要加上以sa[i]为中心的回文串的个数p[sa[i]]。

但是这可能有重复!

我们可以维护一个tmp,也就是上图中蓝色的框。tmp表示以字符sa[i-1]为中心已经被统计过的回文串的个数。

到了当前的sa[i],tmp=min(tmp,h[i]);

每次如果p[x]<=tmp,就continue;

否则,ans+=(p[x]-tmp)/2;(/2是因为有#)

按照最上面的构造方法,每个最长回文串p[i]必定以#开头和结尾,所以可以直接除以2,可以画个图看看。

 

一开始以为z的ascll码<=100,get_sa()那里参数小了。。TLE了好几遍才发现。。

 

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 using namespace std;
  6 
  7 const int N=1000010;
  8 int cl,sl,p[N],rk[N],Rs[N],sa[N],wr[N],y[N],h[N];
  9 char c[N],s[N];
 10 
 11 int minn(int x,int y){return x<y ? x:y;}
 12 int maxx(int x,int y){return x>y ? x:y;}
 13 
 14 void get_sa(int m)
 15 {
 16     for(int i=1;i<=cl;i++) rk[i]=c[i];
 17     for(int i=0;i<=m;i++) Rs[i]=0;
 18     for(int i=1;i<=cl;i++) Rs[rk[i]]++;
 19     for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1];
 20     for(int i=cl;i>=1;i--) sa[Rs[rk[i]]--]=i;
 21     
 22     int ln=1,p=0;
 23     while(p<cl)
 24     {
 25         int k=0;
 26         for(int i=cl-ln+1;i<=cl;i++) y[++k]=i;
 27         for(int i=1;i<=cl;i++) if(sa[i]>ln) y[++k]=sa[i]-ln;
 28         
 29         for(int i=1;i<=cl;i++) wr[i]=rk[y[i]];
 30         for(int i=1;i<=m;i++) Rs[i]=0;
 31         for(int i=1;i<=cl;i++) Rs[wr[i]]++;
 32         for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1];
 33         for(int i=cl;i>=1;i--) sa[Rs[wr[i]]--]=y[i];
 34         
 35         for(int i=1;i<=cl;i++) wr[i]=rk[i];
 36         for(int i=cl+1;i<=cl+ln;i++) wr[i]=0;
 37         p=1;rk[sa[1]]=1;
 38         for(int i=2;i<=cl;i++)
 39         {
 40             if(wr[sa[i]]!=wr[sa[i-1]] || wr[sa[i]+ln]!=wr[sa[i-1]+ln]) p++;
 41             rk[sa[i]]=p;
 42         }
 43         ln*=2,m=p;
 44     }
 45     sa[0]=0;rk[0]=0;
 46 }
 47 
 48 void get_h()
 49 {
 50     int k=0,j;
 51     for(int i=1;i<=cl;i++) if(rk[i]!=1)
 52     {
 53         j=sa[rk[i]-1];
 54         if(k) k--;
 55         while(c[i+k]==c[j+k] && i+k<=cl && j+k<=cl) k++;
 56         h[rk[i]]=k;
 57     }
 58     h[0]=0;
 59 }
 60 
 61 void manacher()
 62 {
 63     int id=0,mx=0;
 64     p[0]=1;
 65     for(int i=1;i<=cl;i++)
 66     {
 67         if(i+p[2*id-i]-1 < mx) p[i]=p[2*id-i];
 68         else
 69         {
 70             p[i]=maxx(0,mx-i+1);
 71             while(c[i+p[i]]==c[i-p[i]] && i+p[i]<=cl && i-p[i]>=1) p[i]++;
 72             if(i+p[i]-1>mx) mx=i+p[i]-1,id=i;
 73         }
 74     }
 75 }
 76 
 77 void solve()
 78 {
 79     int x,tmp=0,ans=0;
 80     for(int i=1;i<=cl;i++)
 81     {
 82         x=sa[i];
 83         tmp=minn(tmp,h[i]);
 84         if(p[x]<=tmp) continue;
 85         ans+=(p[x]-tmp)/2;
 86         tmp=p[x];
 87     }
 88     printf("%d\\n",ans);
 89 }
 90 
 91 int main()
 92 {
 93     freopen("a.in","r",stdin);
 94     // freopen("me.out","w",stdout);
 95     int T;
 96     scanf("%d",&T);
 97     for(int TT=1;TT<=T;TT++)
 98     {
 99         scanf("%s",s+1);
100         sl=strlen(s+1);
101         cl=0;
102         c[++cl]=\'$\';
103         int i;
104         for(i=1;i<=sl;i++) 
105             c[++cl]=\'#\',c[++cl]=s[i];
106         c[++cl]=\'#\';c[++cl]=\'@\';
107         // for(int i=1;i<=cl;i++) printf("%c",c[i]);printf("\\n");
108         // for(int i=1;i<=cl;i++) printf("%d ",p[i]);printf("\\n");
109         manacher();
110         get_sa(200);
111         get_h();
112         printf("Case #%d: ",TT);
113         solve();
114     }
115     return 0;
116 }

 

以上是关于hdu3948-不同回文串的个数后缀数组的主要内容,如果未能解决你的问题,请参考以下文章

HDU4622:Reincarnation(后缀数组,求区间内不同子串的个数)

[HDU3518]Boring counting(后缀数组)

HDU 5008 Boring String Problem ( 后缀数组 )

[BZOJ3676][APIO2014]回文串(Manacher+SAM)

HDU5785 manacher+差分数组

SPOJ 694 || 705 Distinct Substrings ( 后缀数组 && 不同子串的个数 )