回文树简述

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

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

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

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

基本建立思路是建立其前缀的回文树,然后每加上一个字符,统计产生了什么回文;

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

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

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

(好难看)

(点“偶”的fail指向点“奇”)

可看出:

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

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

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

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

几个性质

|s|表示s串的长度;

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

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

构造方法

回文树的基础插入算法:

建立s的回文树;

现在已建立了s的前缀x,然后考虑插入下一个字符;

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

证明:

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

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

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

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

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

这样找的的回文如果还没存在与回文树中,则将其插入;

为了方便这一过程,我们维护每点一个指针fail,s当前的前缀x的最长回文后缀t,没有ctc,就跳fail,直到可以匹配;

建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+1]=pld_t1.s[len+1]='\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来着,呵呵呵哈哈哈哈!!!!!!!!!!

转载于:https://www.cnblogs.com/nietzsche-oier/p/6935081.html

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

  1. Palindromic Tree——回文树(回文自动机)

    Palindromic Tree.中译过来就是--回文树. 那么这个回文树有何功能? 假设我们有一个串S,S下标从0开始,则回文树能做到如下几点: 1.求串S前缀0~i内本质不同回文串的个数(两个串长 ...

  2. 哦也!伟大的回文树(回文自动机)!

    例题引入 例题引入:洛谷P3649 话说马上就要APIO了,litble去看了看历年的APIO题--发现这道题用回文树非常好做,所以就去学了一下回文树. 所谓回文树,就是每个节点代表一个不同的回文串的 ...

  3. 回文树/回文自动机 引入

    回文树/回文自动机引入 在介绍这种数据结构之前,我们来回顾一下处理字符串的一些武器: 1.KMP.AC自动机 2.后缀三姐妹(后缀树,后缀数组,后缀自动机) 3.字符串hash 4.字符串dp 5.m ...

  4. [回文树][BZOJ2160][国家集训队]拉拉队排练

    Description 艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了.拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛.所以作为拉拉队队长的楚雨荨同学知道,帮助篮球队训 ...

  5. 回文树 PAM 学习笔记

    摘自 <2017 年国家集训队论文>" 回文树及其应用 " 一些规定 :Σ\SigmaΣ 表示字符集大小 串 SSS 和字符 ccc,用 ScScSc 表示将 ccc ...

  6. 回文树(回文自动机) - URAL 1960 Palindromes and Super Abilities

     Palindromes and Super Abilities Problem's Link:  http://acm.timus.ru/problem.aspx?space=1&num=1 ...

  7. BZOJ2565 最长双回文子串 回文自动机,回文树

    bzoj2565: 最长双回文串 题意 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为"abc",逆序为"cba" ...

  8. 回文树 / 自动机模板

    const int maxn = 400000; const int N = 26 ;struct Palindromic_Tree {int next[maxn][N] ;//next指针,next ...

  9. 回文串问题的克星——Palindrome Tree(回文树)/Palindrome Automaton(回文自动机)学习小记

    前言 最近B组有一道我不会的题,赶紧学习. 简介 我们知道,Manacher算法可以在 O(n) O ( n ) O(n)的时间内求出以每个位置为中心的最长回文串(虽然我昨天还不知道Manacher算 ...

最新文章

  1. love2d杂记9--光照效果
  2. linux unset path,linux查看和修改PATH环境变量的方法
  3. 拿着锤子找钉子,数字芯片领导者比特大陆进军人工智能
  4. VS2010删除所有断点时不弹出提示窗口
  5. volatile关键字与synchronization关键字的区别?
  6. Fiddler笔记一移动端连接
  7. 如何绘制平台框架的设计图:使用UML工具
  8. 找出数组的最大公约数
  9. Django 【第一篇】基础
  10. python实验室公众号_区块链研究实验室 | 使用Python编写Tendermint应用程序
  11. 凯撒密码 (Python实现)
  12. idea常用快捷键以及自定义快捷键
  13. 太阳天顶角和方位角计算
  14. 【机器学习】层次聚类-Agglomerative clustering
  15. 提高前端生产效率(PxCook工具推荐)
  16. Kettle构建Hadoop ETL实践(八-2):维度表技术
  17. android 常用机型尺寸_Android中图片大小与各种hdpi
  18. MMDetection——GPU训练
  19. (含Matlab源码)算术编码(arithmetic coding)的underflow问题
  20. MTK9652和Mstar938的区别

热门文章

  1. html img 坐标,Html img 标签
  2. sql2016是否支持linux,微软 SQL Server 支持 Linux 了,2017年 中将正式推出
  3. mysql5.7.17版本介绍_mysql5.7.17版本升级源码方式及恢复主主复制
  4. 16位汇编 使用word ptr和byte ptr的寻址方式
  5. VS2010设置全局Include和Lib目录
  6. CentOS设置开机自动启动某服务
  7. Linux系统下如何查看及修改文件读写权限
  8. github生成密钥
  9. px4原生源码学习四--Nuttx 实时操作系统编程
  10. DEBUG各命令详细说明(很详细)