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 ( 后缀数组 )