回文自动机 PAM

回文自动机是一个用来维护一个字符串的回文子串的工具
可以应用于求本质不同回文子串个数,回文子串出现次数,最小回文划分等问题

前置知识

m a n a c h e r \tt manacher manacher
t r i e \tt trie trie

算法用途

维护一个字符串的回文子串

算法复杂度

时间

在末尾添加字符: O ( n ) O(n) O(n)

空间

O ( n × log ⁡ ( ∣ S ∣ ) ) O(n\times \log(|S|)) O(n×log(S))

算法实现

结构

回文自动机由两颗树构成,分别用来存储长度为奇数的回文子串和长度为偶数的回文子串。
t r i e trie trie树 一样,回文自动机的每条边都表示一个字符,每个节点表示一个回文串( t r i e trie trie树可以理解为每个节点表示一个字符串)。
一般回文自动机每个节点都需要储存一个值, s i z i siz_i sizi,代表此节点的回文串的大小。
假设回文自动机有一个节点 u u u,另一个节点 v v v u u u 的儿子, u u u v v v 的边的字符为 c c c s t r i str_i stri 代表节点 i i i 表示的字符串。
那么有 s t r v = c + s t r u + c str_v = c + str_u + c strv=c+stru+c s i z v = s i z u + 2 siz_v=siz_u+2 sizv=sizu+2。所以我们将储存奇回文的树的根节点的 s i z siz siz 设为 − 1 -1 1 ,偶回文的根节点 s i z siz siz 设为 0 0 0
a b b a a abbaa abbaa 的回文自动机(没有 f a i l fail fail指针)如下图所示:
回文自动机

fail 指针

我们设 f a i l u fail_u failu 代表节点 u u u f a i l fail fail 指针指向的节点。
我们有 s t r f a i l u str_{fail_u} strfailu s t r u str_u stru 的后缀,且不存在 s t r v str_v strv,使得 ∣ s t r v ∣ > ∣ s t r f a i l u ∣ |str_v| > |str_{fail_u}| strv>strfailu,满足 s t r v str_v strv 也为 s t r u str_u stru 后缀。(没有回文后缀的回文串的 f a i l fail fail 指向其所在的树的根节点, s i z siz siz 0 0 0 的根节点的 f a i l fail fail 指向 s i z siz siz − 1 -1 1 的根节点, s i z siz siz − 1 -1 1 的根节点没有 f a i l fail fail 指针)

构建

此操作将一个字符插入到原字符串的末尾。( S → S + c S\to S+c SS+c
于是我们只需要对要构建的字符串 S S S 执行 ∣ S ∣ |S| S 次此操作即可构建出 S S S 的回文自动机。

插入一个字符 c c c 时,我们枚举插入前的字符串的每个回文后缀(包括长度为 0 0 0 − 1 -1 1 的),然后判断插入 c c c 之后,这个后缀是否还是一个回文串(只需判断此回文串前面的字符是否与插入的字符相同即可)。

如果是,则这个回文串就是插入 c c c 后字符串的一个回文后缀。我们就在表示这个回文后缀的节点下增加一个节点,表示新产生的这个回文串。如果表示这个回文后缀的节点已有回文串,那么就代表它又出现了一次。

如何快速枚举插入前的字符串的每个回文后缀?我们可以记录一个节点 l a s t last last,节点表示的回文串为插入前的最长回文后缀。
因为 s t r f a i l u str_{fail_u} strfailu s t r u str_u stru 的最长后缀,所以如果我们从 l a s t last last 开始,一直跳 f a i l fail fail,我们走过的每个回文串都是插入前字符串的回文后缀,且遍历顺序为从长到短。

因为遍历顺序为从长到短,我们也可以顺便更新新增节点的 f a i l fail fail指针,和更改 l a s t last last。我们只用把 l a s t last last 更改为第一个通过判断的回文串,每个新增节点的 f a i l fail fail 指向下一个新增的节点即可(如果这个新增节点为最后一个新增节点,我们把它的 f a i l fail fail 指向其所在的树的根节点)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值