回文树专题
学习链接:http://axuhongbo.top/tree/
/*---------------------------------------------
1.len[i]表示编号为i的节点表示的回文串的长度(一个节点表示一个回文串)
2.next[i][c]表示编号为i的节点表示的回文串在两边添加字符c以后变成的回文串的编号(和字典树类似)。
3.fail[i]表示节点i失配以后跳转不等于自身的节点i表示的回文串的最长后缀回文串(和AC自动机类似)。
4.cnt[i]表示节点i表示的本质不同的串的个数(从根节点到当前节点形成的字符串的出现次数)
5.num[i]表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数。(以i为最终节点,往前形成的子串中是回文串的个数。举个例子:babbab,ba是一个,bab是一个。babbba又是一个,所以当num[i]表示以b为结尾的时候,num[i]=3)
6.last指向新添加一个字母后所形成的最长回文串表示的节点。
7.S[i]表示第i次添加的字符(一开始设S[0] = -1(可以是任意一个在串S中不会出现的字符))。
8.p表示添加的节点个数。
9.n表示添加的字符个数。
------------------------------------*/
专题链接:https://cn.vjudge.net/contest/283852#overview
A - The Problem to Slow Down You
题目大意:给你两个字符串,然后问你两个字符串中公共的回文串的数目。
具体思路:首先对第一个字符串进行建树,记录一下每一个本质不同的回文串的个数,然后再对第二个字符串进行操作,数还是按照第一个字符串建好的树进行计数,然后数一下这个字符串在原来的树上形成的回文串的个数,然后两个个数分别相乘累加就可以了。
AC代码:
1 #include<iostream> 2 #include<stack> 3 #include<cstring> 4 #include<iomanip> 5 #include<stdio.h> 6 #include<algorithm> 7 #include<cmath> 8 using namespace std; 9 # define ll long long 10 # define inf 0x3f3f3f3f 11 const int maxn = 4e5+100; 12 char str1[maxn]; 13 char str2[maxn]; 14 struct Palindromic_Tree 15 { 16 int nex[maxn][30]; 17 int fail[maxn]; 18 ll cnt1[maxn],cnt2[maxn]; 19 int num[maxn]; 20 int len[maxn]; 21 int s[maxn]; 22 int last,len1,len2; 23 int n; 24 int p; 25 int newnode(int l) 26 { 27 for(int i=0; i<30; i++) 28 nex[p][i]=0; 29 num[p]=0; 30 len[p]=l; 31 return p++; 32 } 33 void init() 34 { 35 for(int i=0;i<=len1+len2+4;i++){ 36 cnt1[i]=cnt2[i]=0; 37 } 38 p=0; 39 newnode(0); 40 newnode(-1); 41 last=0; 42 n=0; 43 s[n]=-1; 44 fail[0]=1; 45 } 46 void init1() 47 { 48 last=0; 49 n=0; 50 s[n]=-1; 51 fail[0]=1; 52 } 53 int getfail(int x) 54 { 55 while(s[n-len[x]-1]!=s[n]) 56 x=fail[x]; 57 return x; 58 } 59 void add(int c) 60 { 61 c-='a'; 62 s[++n]=c; 63 int cur=getfail(last); 64 if(!nex[cur][c]) 65 { 66 int now=newnode(len[cur]+2); 67 fail[now]=nex[getfail(fail[cur])][c]; 68 nex[cur][c]=now; 69 num[now]=num[fail[now]]+1; 70 } 71 last=nex[cur][c]; 72 cnt1[last]++; 73 } 74 void add1(int c) 75 { 76 c-='a'; 77 s[++n]=c; 78 int cur=getfail(last); 79 if(!nex[cur][c]) 80 { 81 int now=newnode(len[cur]+2); 82 fail[now]=nex[getfail(fail[cur])][c]; 83 nex[cur][c]=now; 84 num[now]=num[fail[now]]+1; 85 } 86 last=nex[cur][c]; 87 cnt2[last]++; 88 } 89 void count1() 90 { 91 for(int i=p-1; i>=0; i--) 92 cnt1[fail[i]]+=cnt1[i]; 93 } 94 void count2() 95 { 96 for(int i=p-1; i>=0; i--) 97 { 98 cnt2[fail[i]]+=cnt2[i]; 99 } 100 } 101 } q; 102 int main() 103 { 104 int T; 105 int Case=0; 106 scanf("%d",&T); 107 while(T--) 108 { 109 scanf("%s",str1); 110 scanf("%s",str2); 111 q.len1=strlen(str1); 112 q.len2=strlen(str2); 113 q.init(); 114 for(int i=0; i<q.len1; i++) 115 { 116 q.add(str1[i]); 117 } 118 q.init1(); 119 for(int i=0; i<q.len2; i++) 120 { 121 q.add1(str2[i]); 122 } 123 q.count1(); 124 q.count2(); 125 ll ans=0; 126 printf("Case #%d: ",++Case); 127 for(int i=2; i<=q.p-1; i++) 128 { 129 ans+=q.cnt1[i]*q.cnt2[i]; 130 } 131 printf("%lld\n",ans); 132 } 133 return 0; 134 }
B - 最长回文
题目大意:让你求给定的字符串中最长的回文字符子串。
具体思路:每插入一个字符,求插入的以这个字符结尾形成的最长的回文子串就可以了。
AC代码:
1 #include<iostream> 2 #include<stack> 3 #include<cstring> 4 #include<iomanip> 5 #include<stdio.h> 6 #include<algorithm> 7 #include<cmath> 8 using namespace std; 9 # define ll long long 10 # define inf 0x3f3f3f3f 11 const int maxn = 2e5+100; 12 char str[maxn]; 13 struct Palindromic_Tree 14 { 15 int nex[maxn][30]; 16 int fail[maxn]; 17 ll cnt[maxn]; 18 int num[maxn]; 19 int len[maxn]; 20 int s[maxn]; 21 int last; 22 int n; 23 int p; 24 int newnode(int l) 25 { 26 for(int i=0; i<30; i++) 27 nex[p][i]=0; 28 cnt[p]=0; 29 num[p]=0; 30 len[p]=l; 31 return p++; 32 } 33 void init() 34 { 35 p=0; 36 newnode(0); 37 newnode(-1); 38 last=0; 39 n=0; 40 s[n]=-1; 41 fail[0]=1; 42 } 43 int getfail(int x) 44 { 45 while(s[n-len[x]-1]!=s[n]) 46 x=fail[x]; 47 return x; 48 } 49 void add(int t,int c) 50 { 51 c-='a'; 52 s[++n]=c; 53 int cur=getfail(last); 54 // cout<<t<<" "<<cur<<endl; 55 if(!nex[cur][c]) 56 { 57 int now=newnode(len[cur]+2); 58 fail[now]=nex[getfail(fail[cur])][c]; 59 // cout<<cur<<" "<<fail[cur]<<endl; 60 // cout<<11111<<" "<<getfail(fail[cur])<<endl; 61 nex[cur][c]=now; 62 num[now]=num[fail[now]]+1; 63 } 64 last=nex[cur][c]; 65 cnt[last]++; 66 } 67 void cont() 68 { 69 for(int i=p-1; i>=0; i--) 70 cnt[fail[i]]+=cnt[i]; 71 } 72 } q; 73 int main() 74 { 75 int T; 76 scanf("%d",&T); 77 while(~scanf("%s",str)) 78 { 79 int maxx=0; 80 int len=strlen(str); 81 q.init(); 82 for(int i=0; i<len; i++) 83 { 84 q.add(i,str[i]); 85 maxx=max(q.len[q.last],maxx); 86 } 87 printf("%d\n",maxx); 88 } 89 return 0; 90 }
C - The Number of Palindromes
题目大意:给你一个字符串,让你求这个字符串中本质不同的回文串的个数(长度不同,或者长度相同回文串排布不完全相同)。
具体思路:回文树中p-2就是本质不同的回文串的个数,因为如果有重复的就一定能用形成的回文树表示。
AC代码:
1 #include<iostream> 2 #include<stack> 3 #include<cstring> 4 #include<iomanip> 5 #include<stdio.h> 6 #include<algorithm> 7 #include<cmath> 8 using namespace std; 9 # define ll long long 10 # define inf 0x3f3f3f3f 11 const int maxn = 2e5+100; 12 char str[maxn]; 13 struct Palindromic_Tree 14 { 15 int nex[maxn][30]; 16 int fail[maxn]; 17 ll cnt[maxn]; 18 int num[maxn]; 19 int len[maxn]; 20 int s[maxn]; 21 int last; 22 int n; 23 int p; 24 int newnode(int l) 25 { 26 for(int i=0; i<30; i++) 27 nex[p][i]=0; 28 cnt[p]=0; 29 num[p]=0; 30 len[p]=l; 31 return p++; 32 } 33 void init() 34 { 35 p=0; 36 newnode(0); 37 newnode(-1); 38 last=0; 39 n=0; 40 s[n]=-1; 41 fail[0]=1; 42 } 43 int getfail(int x) 44 { 45 while(s[n-len[x]-1]!=s[n]) 46 x=fail[x]; 47 return x; 48 } 49 void add(int t,int c) 50 { 51 c-='a'; 52 s[++n]=c; 53 int cur=getfail(last); 54 // cout<<t<<" "<<cur<<endl; 55 if(!nex[cur][c]) 56 { 57 int now=newnode(len[cur]+2); 58 fail[now]=nex[getfail(fail[cur])][c]; 59 // cout<<cur<<" "<<fail[cur]<<endl; 60 // cout<<11111<<" "<<getfail(fail[cur])<<endl; 61 nex[cur][c]=now; 62 num[now]=num[fail[now]]+1; 63 } 64 last=nex[cur][c]; 65 cnt[last]++; 66 } 67 void cont() 68 { 69 for(int i=p-1; i>=0; i--) 70 cnt[fail[i]]+=cnt[i]; 71 } 72 } q; 73 int main() 74 { 75 int T; 76 scanf("%d",&T); 77 while(~scanf("%s",str)) 78 { 79 int maxx=0; 80 int len=strlen(str); 81 q.init(); 82 for(int i=0; i<len; i++) 83 { 84 q.add(i,str[i]); 85 maxx=max(q.len[q.last],maxx); 86 } 87 printf("%d\n",maxx); 88 } 89 return 0; 90 }
D - 最长双回文串
题目大意:顺序和逆序读起来完全一样的串叫做回文串。比如 acbca 是回文串,而 abc 不是( abc 的顺序为“abc” ,逆序为 “cba” ,不相同)。
输入长度为 n 的串 S ,求 S 的最长双回文子串 T, 即可将 T 分为两部分 X , Y ,( |X|,|Y|≥1 )且 X 和 Y 都是回文串。然后给你一个字符串,让你找出这个字符串中的最的双回文串。
具体思路:我们枚举每一个位置,最长的双回文串的形成是以i为结尾的回文串+以i+1为开头的回文串,在找以i+1为开头的回文串的时候,我们将字符串倒过来再建一遍树就可以了。我们枚举每一个位置,然后遍历找出最大的就可以了。
AC代码:
1 #include<iostream> 2 #include<stack> 3 #include<cstring> 4 #include<iomanip> 5 #include<stdio.h> 6 #include<algorithm> 7 #include<cmath> 8 using namespace std; 9 # define ll long long 10 # define inf 0x3f3f3f3f 11 const int maxn = 4e5+100; 12 char str[maxn]; 13 struct Palindromic_Tree 14 { 15 int nex[maxn][30]; 16 int fail[maxn]; 17 ll cnt[maxn]; 18 int num[maxn]; 19 int len[maxn]; 20 int s[maxn]; 21 int pre[maxn]; 22 int last; 23 int n; 24 int p; 25 int newnode(int l) 26 { 27 for(int i=0; i<30; i++) 28 nex[p][i]=0; 29 num[p]=0; 30 len[p]=l; 31 return p++; 32 } 33 void init() 34 { 35 p=0; 36 newnode(0); 37 newnode(-1); 38 last=0; 39 n=0; 40 s[n]=-1; 41 fail[0]=1; 42 } 43 int getfail(int x) 44 { 45 while(s[n-len[x]-1]!=s[n]) 46 x=fail[x]; 47 return x; 48 } 49 void add(int c,int i) 50 { 51 c-='a'; 52 s[++n]=c; 53 int cur=getfail(last); 54 if(!nex[cur][c]) 55 { 56 int now=newnode(len[cur]+2); 57 fail[now]=nex[getfail(fail[cur])][c]; 58 nex[cur][c]=now; 59 num[now]=num[fail[now]]+1; 60 } 61 last=nex[cur][c]; 62 pre[i]=len[last]; 63 } 64 } q1,q2; 65 int main() 66 { 67 q1.init(); 68 q2.init(); 69 scanf("%s",str+1); 70 int len=strlen(str+1); 71 for(int i=1;i<=len;i++){ 72 q1.add(str[i],i); 73 } 74 reverse(str+1,str+len+1); 75 for(int i=1;i<=len;i++){ 76 q2.add(str[i],i); 77 } 78 int maxx=0; 79 for(int i=1;i<len;i++){ 80 if(maxx<q1.pre[i]+q2.pre[len-i]) 81 cout<<i<<endl; 82 maxx=max(maxx,q1.pre[i]+q2.pre[len-i]); 83 } 84 printf("%d\n",maxx); 85 return 0; 86 }