回文树专题

学习链接: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
",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
",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
",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
",maxx);
85     return 0;
86 }
原文地址:https://www.cnblogs.com/letlifestop/p/10397314.html