想了想 还是要先把字符串的东西先都学完告一段落了再说 时间不多了 加油~。
PAM 回文自动机 比SAM简单到不知道哪里去了。
回文自动机和其他自动机一样有字符集 有状态 有转移。
一个字符串的回文自动机的一个最显然的性质是可以识别一个字符串中所有回文子串 这是最有价值的性质。
特点:有两个根 一个代表偶回文串的根 一个代表奇回文串的根 具体的 每个点 同SAM的节点一样都有len fail t[x][c]数组。
可以说 fail指针是所有字符串自动机的精髓吧... 值得一提的是 奇数根的len为-1 偶数根的len为0。
另外 这里我说一下我对fail的理解 这个就很有意思了这里规定偶数根的标号为0 奇数根的编号为1 偶数根的fail为1 奇数根的fail为1
对了,fail在回文自动机里的意思是指最长公共回文后缀。偶数根的fail之所以是1是因为接下来只有奇数根的那个位置能够满足了。
奇数根的fail是无效的 因为只要跳到奇数根就意味着一定是合法的了.
还是采用增量法 来构造这个回文自动机。 考虑 上一次构造出来的节点是 p 当前字符为s[i]
那么显然 我们当前这个节点由于是要加到p的后面的 所以 我们要看 s[i-len[p]-1] 和 s[i]是否是相等的 如果相等那么就可以安排一下p的儿子了。
不相等呢 我们考虑围绕这个p搞一些事情 可能存在其他更小的回文串满足这个性质 那么显然我们对p不断的寻找 然后找到一个p满足上述条件。
一直都不满足?最后会跳到奇数根 就满足了。考虑一下更新的信息 设np是当前新建节点 显然 len[np]=len[p]+2;
fail[np]怎么求呢?fail[np]显然我们继续跳p的fail指针然后找到下一个满足条件的点 就是我们当前的fail[np]了。
这里有好几个小细节 如 如果不存在fail[np]那么这个fail[np]是0 还是1 呢 应该是0的是1的话就不对了因为这个地方其实就是这样的fail如果没有实际的点一般都是0而并非1 这样可以让下一个来的字符先满足长度为2的回文串。
还有一点是 为什么这样做事最长的 归纳一下吧 显然 fail[p]按照定义是最长的 那么 fail[np]在fail[p]下也应该是最长的。
一个细节:我们虽然新建了np但是我们不能直接让p连向np 因为如果p是1的话跳fail会跳到np自己的fail指向自己就不太对了吧...
下面是 一个回文自动机可以表示出一个字符串的所有回文子串的证明:
增量法构造的时 新来一个字符c 可能此时形成了多个回文串我们考虑一下最长的那个回文串是否被建好了 如果最长的已经建好了 说明那些较短的回文串也被建好了,因为这个回文串中肯定是包含其他的回文串的,也就是增量到对应的节点的时候这里指回文串的右边显然已经被构造好了所以得证。
如果最长的没有被建好 那么建出来 然后其他的野已经被建好了 还是回文串的右边在增量的时候...得证.
最后 好像还有人求了一发 trans指针 我不知道这个有啥用 但是这个trans指针还是非常容易求出的.
trans指针:小于等于当前节点长度的一半的最长回文后缀。
显然 当len[p]<=2 时 trans[p]=fail[p];
那么考虑更一般的情况 新建节点np 的trans[np]=? 抓住关键...从trans[p]开始跳fail即可且保证能扩展和len满足要求即可...
PAM其实就这么多的东西我的理解我觉得没有什么偏差...对了边数和点数都是O(n)级别的 点数显然 边数也显然...时间复杂度我不会证明但是是O(n)的...
说完了上面 你可能会惊奇的发现 一个字符串的本质不同的回文子串的个数是O(n)级别的.空间的话觉得不太稳开两倍.
至此完结~ 下面可能会放几道有意思的题目...
[SHOI2011双倍回文](https://www.luogu.com.cn/problem/P4287)
给定一个字符串 求这个字符串的最长双倍回文子串的长度,最长双倍回文子串的定义:长度是4的倍数,前半部分是回文的,后半部分也是回文的,且整体还是回文的.
看起来很不好做的样子... 分析一下性质吧 有意思的是 双倍回文子串的两半都必须是偶回文串 因为不是的话不可能是4的倍数.
解法也就出现了 遍历每一个回文串 惊奇的发现 利用trans指针就可以解决这个问题!由于我们遍历了所有的本质不同字符串所以显然可以求得答案.
[最长双回文串](https://www.luogu.com.cn/problem/P4555)
这道题也很简单 但是却有一些小套路在里面 两端都是回文串 一个比较容易想到的是 枚举断点i 求以i为结尾最长的和以i+1位开头的最长的回文串加起来即可。
这思想在很多题目中都有应用。
[CERC2014 Virus synthesis](https://www.luogu.com.cn/problem/P4762)
多组数据 每组数据给定一个字符集为{A,G,C,T}的字符串T 初始有一个空串 每次可以在串的开头或结尾加上一个字符 或者在串开头或末尾加上一个该串的逆串.
求最小操作次数。显然是不可以二分的,因为构造出这个字符串我们可以直接求出二分再构造无疑是不必要的。
由于是串的逆序 可以发现显然应该是T的回文子串 但是是整个字符串的回文 所以我们肯定是先构造出某个回文串剩下的再暴力构造。
这就又需要我们的trans指针了 好像这样dp下一下就没了...但是 我没想到一个转移啊啊啊
f[i]=min(f[i],f[f(i)]+1)这个转移没写 我想半天以为思路出错了 结果是根本就没有完善好思路 哎.
考虑一个性质 我们不单需要trans指针 也可能是直接的暴力累加过来的...我是dd.