const int maxn = 400000;
const int N = 26 ;struct Palindromic_Tree {int next[maxn][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成int fail[maxn] ;//fail指针,失配后跳转到fail指针指向的节点int cnt[maxn] ;//第 i 号节点表示的回文串出现的次数、注意最后调用 count 函数完成计算int num[maxn] ;//以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数(未经验证)int len[maxn] ;//len[i]表示节点i表示的回文串的长度int S[maxn] ;//存放添加的字符int last ;//指向上一个字符所在的节点,方便下一次addint n ;//字符数组指针int tot ;//节点指针int newnode ( int l ) {//新建节点for ( int i = 0 ; i < N ; ++ i ) next[tot][i] = 0 ;cnt[tot] = 0 ;num[tot] = 0 ;len[tot] = l ;return tot ++ ;}void init () {//初始化tot = 0 ;newnode (  0 ) ;newnode ( -1 ) ;last = 0 ;n = 0 ;S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判fail[0] = 1 ;}int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;return x ;}void add ( int c ) {c -= 'a' ;S[++ n] = c ;int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串int now = newnode ( len[cur] + 2 ) ;//新建节点fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转next[cur][c] = now ;num[now] = num[fail[now]] + 1 ;}last = next[cur][c] ;cnt[last] ++ ;}void count () {for ( int i = tot - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
    }
}PAM1, PAM2;

单向插入版

struct Palindromic_Tree {static const int maxn = 400000;static const int Letter = 26 ;deque<int> text;int len[maxn];///len[i]表示节点i表示的回文串的长度int cnt[maxn];///第 i 节点表示的回文串出现的次数、注意最后调用 getCnt 函数完成计算int num[maxn];///表示以节点 i 代表的回文串端点为端点的不同回文串个数int nxt[maxn][Letter];///nxt静态指针和字典树类似,指向的串为当前串两端加上同一个字符构成int fail[maxn];///失配指针int odd, even;///奇偶原始节点int pre, suf;int totNode;///自动机所有节点的个数LL totNum;///串中所有回文串数量int newNode(int _len){int ret = totNode++;len[ret] = _len;cnt[ret] = num[ret] = 0;fail[ret] = 0;memset(nxt[ret], 0, sizeof(nxt[ret]));return ret;}inline void init(){text.clear();totNode = 0;totNum = 0;even = newNode(0);odd = newNode(-1);fail[even] = fail[odd] = odd;pre = suf = even;}int encode(char c){return c - 'a';}template<typename FunT>int getFail(int ch, int cur, FunT getNext){for(int i = getNext(len[cur]);i < 0 || i >= (int)text.size() || text[i] != ch;cur = fail[cur], i = getNext(len[cur]));return cur;}template<typename FunT>int Insert(int ch, int last, FunT getNext){last = getFail(ch, last, getNext);if(!nxt[last][ch]){int cur = newNode(len[last] + 2);fail[cur] = nxt[getFail(ch, fail[last], getNext)][ch];nxt[last][ch] = cur;num[cur] = num[fail[cur]] + 1;}last = nxt[last][ch];totNum += (LL)num[last];cnt[last]++;return last;}int push_front(char c){text.push_front(encode(c));pre = Insert(encode(c), pre, [](int i)->int{ return i + 1; });if(len[pre] == (int)text.size()) suf = pre;return totNode;}int push_back(char c){text.push_back(encode(c));suf = Insert(encode(c), suf, [this](int i)->int{ return this->text.size() - i - 2; });if(len[suf] == (int)text.size()) pre = suf;return totNode;}int push_front(char *s){int ret = 0;for(int i=0; s[i]; i++)ret = push_front(s[i]);return ret;}int push_back(char *s){int ret = 0;for(int i=0; s[i]; i++)ret = push_back(s[i]);return ret;}inline void getCnt(){for(int i=totNode-1; i>=0; i--)cnt[fail[i]] += cnt[i];}}PAM;

双向插入版

回文树的功能

1.求串S前缀0~i内本质不同回文串的个数

(两个串长度不同或者长度相同且至少有一个字符不同便是本质不同)

对于每一个前缀、创建过程中、自动机节点数 - 2 便是答案

2.求串S内每一个本质不同回文串出现的次数

自动机模板中的 cnt 数组记录的就是这个信息

3.求串S内回文串的个数(其实就是1和2结合起来)

自动机模板中 cnt 数组之和

4.求以下标 i 结尾的回文串的个数

一个结论

一个串本质不同的回文串数量是 O(n) 级别的

由于回文树上每个节点都表示一个回文串

所以这颗树的节点不会超过串的长度

参考博客

电子科大算法讲堂——回文树

转载于:https://www.cnblogs.com/LiHior/p/9628885.html

回文树 / 自动机模板相关推荐

  1. 回文树(模板)+ 例题

    引用: Palindromic Tree--回文树[处理一类回文串问题的强力工具] 回文树练习题集 首先,回文树有何功能? 假设我们有一个串S,S下标从0开始,则回文树能做到如下几点: 1.求串S前缀 ...

  2. 2018 ICPC 南京 M. Mediocre String Problem(ExKMP + Manacher / ExKMP+回文树)

    2018 ICPC 南京 全文见:https://blog.csdn.net/qq_43461168/article/details/112796538 M. Mediocre String Prob ...

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

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

  4. 回文自动机 / 回文树

    B站讲解视频 B站视频对于pre数组(fail数组)的讲解较清晰,可供参考. 参考博客 模板 #include <queue> #include <cstdlib> #incl ...

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

    回文树简述 在大部分说法中,回文树与回文自动机指的是一个东西: 回文树是对一个字符串,基于自动机思想构建的处理回文问题的树形结构: 回文树是对着一个单串建立的: 于是他主要用于计数(回文子串种类及个数 ...

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

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

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

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

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

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

  9. 「学习笔记」回文树/回文自动机(Palindromic Tree)

    引入 有时候题目要求一些这样的问题 1. 求以串 s s s 本质不同的回文串个数(即长度不同或长度相同且至少有一个字符不相同的字符串) 2. 求以位置i" role="prese ...

最新文章

  1. [C#][Newtonsoft.Json] Newtonsoft.Json 序列化时的一些其它用法
  2. S3C6410的Bootloader的两个阶段BL1和BL2编译相关学习
  3. Java集合系列---TreeMap源码解析(巨好懂!!!)
  4. canvas width/height和style.width/style.height
  5. 如何用思维导图快速理解PMBOK-PMP第六版教材
  6. 隐式函数声明警告---调用malloc函数但不包含头文件
  7. 3DGPS数据图和3D圆轨道图
  8. 利用VMM建立基于事务的层次化验证平台
  9. 泰山OFFICE技术讲座:英寸,厘米,磅,派卡,提,行,字行,像素的换算关系
  10. 古代银票里的加密知识
  11. android 动态贴纸,萌拍动态贴纸相机软件下载
  12. 算法导论-----最长公共子序列LCS(动态规划)
  13. ldd 执行结果:不是动态可执行文件
  14. 学生管理系统详细架构
  15. 计算机专业surface pro,surface pro4家庭版和专业版的不同
  16. 计算机病毒攻击战是指将什么病毒,计算机病毒攻击战是指将( )植入敌方计算机系统,删除其数据,瘫痪其系统。...
  17. GO冷门又重要的知识点(一)
  18. StringBuilder后加换行符
  19. Vue - 实现微信扫码登录功能(项目植入微信扫码登录功能)超详细完整流程详解及详细代码及注释,附带完整功能源码、常见问题解决方案
  20. 踏入数学天堂的钥匙-微积分

热门文章

  1. flex右对齐_移动WEB开发 — Flex布局
  2. android webview缩放功能,在Android WebView中启用/禁用缩放
  3. java bridge 模式_学习:java设计模式—Bridge模式
  4. (17/24) webpack实战技巧:生产环境和开发环境并行设置,实现来回切换
  5. 全面掌握ping命令(三) ping命令防火墙设置
  6. 差分硬盘的merge(合并差异)实验分析
  7. MongoDB游标操作(4)
  8. c语言实现数据结构中的链式表
  9. 杭电--N!(大数)
  10. linux 添加硬盘