回文树或者回文自动机,及相关例题

回文树简述

在大部分说法中,回文树与回文自动机指的是一个东西;

回文树是对一个字符串,基于自动机思想构建的处理回文问题的树形结构;

回文树是对着一个单串建立的;

于是他主要用于计数(回文子串种类及个数)

基本建立思路是先建立其前缀的回文树,然后每加上一个字符,统计影响;

回文树存在fail指针但一般不承接字符串匹配问题;

(回文树大概可以判定一个回文串是不是一个串的子串,但KMP之类的可以做得更好)

构建好的回文树,是这样的:

(好难看)

可看出:

存在两个树结构,分别记录奇数|偶数长度的回文;

每个点记录一种字符串(但是由于可以通过根到该节点的路径确定这个字符串是什么,于是并不需要在该节点记录这个信息)

每个节点连字符x边向一个子节点,表示在她的左右两边加x构成的回文是这个总字符串的子串(根节点相关部分单独定义);

每个节点连一条fail指针向其最长子回文;

几个性质

节点数不超过|s|:每个回文是互不相同的回文,每个回文都是某一个点的最长后缀回文,而每个点的最长后缀回文是唯一的;

转移数是O(|s|)级的:树有节点数-2个边(因为是两棵树),加上每个节点一个fail,于是是O(|s|)级的;

|s|表示s串的长度;

构造方法

回文树的基础插入算法:

建立s的回文串;

先建s的某前缀x,然后考虑插入下一个字符;

每次插入最多只会把她的最长后缀回文贡献到树中;

证明:

c除最长后缀回文之外的后缀回文i是其最长后缀回文k的子串,则她关于k的中心有一个对称串j,由于k本身是对称的,于是j与i本质相同,由于j以加入回文树中,于是i不必加入;

那么我们设x串之前的最长后缀回文是t,在加入c后,我们期望的c的最长后缀回文最长是ctc,

但t串前面不是字符c怎么办,

试试t的除自己外最长回文后缀的前一个字符是不是c,

若不行,再试试她的最长回文后缀。。。。

于是我们才需要向回文串的最长回文后缀加fail;

所以我们维护一个指针last,s当前的前缀x的最长回文后缀t,没有ctc,就跳fail,直到可以匹配;

然后last指向当前在的节点;

通过势能分析法发现这个算法是每次插入均摊O(1),总共O(n)的;

例题

BZOJ P2565 最长双回文串

对读入的字符串,正反分别建回文树,在此同时分别记录以每个为起点|终点的最长前|后缀回文;

枚举断点,求最值即可;

 1 #include<cstdio>
 2 #include<cstring>
 3 using namespace std;
 4 struct dt{
 5     int ch[30],fail,len;
 6 };
 7 struct Pld_T{
 8     int tot,last;
 9     dt data[500500];
10     int len[500500];
11     char s[500500];
12     void Init(){
13         data[0].fail=1;
14         data[1].len=-1;
15         tot=1;last=0;
16     }
17     void insert(int x){
18         int j;
19         while(s[x-data[last].len-1]!=s[x])last=data[last].fail;
20         if(!data[last].ch[s[x]-‘a‘]){
21             data[++tot].len=data[last].len+2;
22             j=data[last].fail;
23             while(s[x-data[j].len-1]!=s[x])j=data[j].fail;
24             data[tot].fail=data[j].ch[s[x]-‘a‘];
25             data[last].ch[s[x]-‘a‘]=tot;
26             last=tot;
27         }
28         else
29             last=data[last].ch[s[x]-‘a‘];
30         len[x]=data[last].len;
31     }
32 };
33 Pld_T pld_t1,pld_t2;
34 int main()
35 {
36     int i,j,k,len,ans=0;
37     pld_t1.Init();pld_t2.Init();
38     scanf("%s",pld_t1.s+1);
39     len=strlen(pld_t1.s+1);
40     for(i=len,j=1;i>=1;i--,j++)
41         pld_t2.s[j]=pld_t1.s[i];
42     pld_t2.s[0]=pld_t1.s[0]=‘#‘;
43     pld_t2.s[len]=pld_t1.s[len]=‘\0‘;
44     for(i=1;i<=len;i++){
45         pld_t1.insert(i);
46         pld_t2.insert(i);
47     }
48     for(i=1;i<len;i++)
49         if(ans<pld_t1.len[i]+pld_t2.len[len-i])
50             ans=pld_t1.len[i]+pld_t2.len[len-i];
51     printf("%d",ans);
52     return 0;
53 }

BZOJ P3676 [Apio2014]回文串

对每个点维护cnt表示她是几个字符的最长回文后缀;

那么他出现的次数就是她作为自己的末端字符的最长回文后缀出现的次数,以及她作为自己的末端字符的最长回文后缀的回文后缀(也不比是除她外最长的)出现的次数;

见代码

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 char s[300010];
 6 struct Pld_T{
 7     int ch[26],fail,len,cnt;
 8 };
 9 Pld_T pld_t[300010];
10 int tot;
11 int main()
12 {
13     int i,j,k,len;
14     long long ans=0;
15     scanf("%s",s+1);
16     len=strlen(s+1);s[0]=‘#‘;
17     pld_t[0].fail=1;k=0;pld_t[1].len=-1;tot=1;
18     for(i=1;i<=len;i++){
19         while(s[i-pld_t[k].len-1]!=s[i])k=pld_t[k].fail;
20         if(!pld_t[k].ch[s[i]-‘a‘]){
21             pld_t[++tot].len=pld_t[k].len+2;
22             j=pld_t[k].fail;
23             while(s[i-pld_t[j].len-1]!=s[i])j=pld_t[j].fail;
24             pld_t[tot].fail=pld_t[j].ch[s[i]-‘a‘];
25             pld_t[k].ch[s[i]-‘a‘]=tot;
26         }
27         k=pld_t[k].ch[s[i]-‘a‘];
28         pld_t[k].cnt++;
29     }
30     for(i=tot;i>=2;i--){
31         pld_t[pld_t[i].fail].cnt+=pld_t[i].cnt;
32         if((long long)pld_t[i].cnt*pld_t[i].len>ans)
33             ans=(long long)pld_t[i].cnt*pld_t[i].len;
34     }
35     printf("%lld",ans);
36     return 0;
37 }

我之前想在fail树上建LCT来着,呵呵呵哈哈哈哈!!!!!!!!!!

时间: 2024-11-05 13:39:29

回文树或者回文自动机,及相关例题的相关文章

Palindromic Tree 回文自动机-回文树 例题+讲解

---恢复内容开始--- 回文树,也叫回文自动机,是2014年被西伯利亚民族发明的,其功能如下: 1.求前缀字符串中的本质不同的回文串种类 2.求每个本质不同回文串的个数 3.以下标i为结尾的回文串个数/种类 4.每个本质不同回文串包含的本质不同回文串种类 (本文参考自Palindromic Tree——回文树[处理一类回文串问题的强力工具],Palindromic Tree 回文自动机-回文树 解决回文串的神器) 下面介绍一些数组的意义 next[][]类似于字典树,指向当前字符串在两段同时加

回文树

(没有坑怎么填?) 最近膜了一些关于回文串的题目,感到非常有意思,遂开篇记录. 在逛UOJ的题目时发现了vfk添上了新题,APIO 2014的题目.本身是一件很正常的事,而它事实上也没有变成什么了不得的事.我看到了Palindrome这个标题---回文串已经烂大街了,没什么新意.不过我很早就向学习回文树这东西了,久仰其大名而未尝真正去了结果它,于是我就顺手撸了一把豪哥的论文,发现他讲解的实在是晦涩难懂---论文的通病,就是虽然表述没有歧义,但是难以理解.嘛,然后我就找了几个标程,发现回文树这东西

P3649 [APIO2014]回文串(回文树)

题目描述 给你一个由小写拉丁字母组成的字符串 ss .我们定义 ss 的一个子串的存在值为这个子串在 ss 中出现的次数乘以这个子串的长度. 对于给你的这个字符串 ss ,求所有回文子串中的最大存在值. 输入输出格式 输入格式: 一行,一个由小写拉丁字母(a~z)组成的非空字符串 ss . 输出格式: 输出一个整数,表示所有回文子串中的最大存在值. 输入输出样例 输入样例#1: 复制 abacaba 输出样例#1: 复制 7 输入样例#2: 复制 www 输出样例#2: 复制 4 说明 [样例解

计蒜客 2018南京网络赛 I Skr ( 回文树 )

题目链接 题意 : 给出一个由数字组成的字符串.然后要你找出其所有本质不同的回文子串.然后将这些回文子串转化为整数后相加.问你最后的结果是多少.答案模 1e9+7 分析 : 应该可以算是回文树挺裸的题目吧 可惜网络赛的时候不会啊.看着马拉车想半天.卒... 对于每一个节点.记录其转化为整数之后的值 然后在回文串插入字符的时候 不断维护这个信息就行了 其实很好理解.看一下代码就懂了 ( 如果你学过回文树的话... ) 就是多加了变量 val .维护语句 #include<bits/stdc++.h

回文树总结

回文树介绍看这 : 点击 回文树 首先,回文树有何功能?假设我们有一个串S,S下标从0开始,则回文树能做到如下几点: 1.求串S前缀0~i内本质不同回文串的个数(两个串长度不同或者长度相同且至少有一个字符不同便是本质不同)2.求串S内每一个本质不同回文串出现的次数3.求串S内回文串的个数(其实就是1和2结合起来)4.求以下标i结尾的回文串的个数 模板: const int MAXN = 100005 ; const int N = 26 ; struct Palindromic_Tree { /

回文树(模板)

一.复杂度 构造回文树需要的空间复杂度为O(N*字符集大小),时间复杂度为O(N*log(字符集大小)) 二.应用 1.求串S前缀0~i内本质不同回文串的个数(两个串长度不同或者长度相同且至少有一个字符不同便是本质不同) 2.求串S内每一个本质不同回文串出现的次数 3.求串S内回文串的个数(其实就是1和2结合起来) 4.求以下标i结尾的回文串的个数 三.简单说明 int next[MAXN][26] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成 int f

[算法模版]回文树

回文树 本文全文引自yijan,特此鸣谢. 回文树,也就是回文自动机,PAM(Palindrome automaton) 是一个处理回文串的有力工具.然而这个东西比SAM简单多了.. (它可能比 manacher 要强得多?) 回文自动机有两个根,也就是说其实是有两个树的,一个存储长度为奇数的回文串一个存储长度为偶数的回文串. 回文自动机上的每一个节点表示一个本质不同的回文串.也就是说回文自动机上的节点个数就是本质不同的回文串个数. 一些定义 $ len[p] $ 表示 $ p $ 节点所代表回

bzoj3676: [Apio2014]回文串 回文树

回文树的裸题. #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=500100; const int INF=1e9+10; struct PalinTree { int ch[maxn][26],f[maxn]; int

HDU3948 &amp; 回文树模板

Description: 求本质不同回文子串的个数 Solution: 回文树模板,学一学贴一贴啊... Code: /*================================= # Created time: 2016-04-20 20:55 # Filename: hdu3948.cpp # Description: =================================*/ #define me AcrossTheSky&HalfSummer11 #include &l