洛谷P5496 回文自动机【PAM】模板
阅读原文时间:2023年07月08日阅读:1

回文自动机模板

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

2.回文自动机的状态数不超过串长,且状态数等于本质不同的回文串数量,除了奇偶两个根节点

3.如何统计所有回文串的数量,类似后缀自动机,不需要重新拓扑排序,因为是按节点顺序插入的,所以逆序上传即可

建树的时候\(0\)节点为偶数长度回文的根

\(1\)节点为奇数长度回文的根

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 5e5+7;
class PAM{
private:
    int tot,sz[MAXN],len[MAXN],ch[MAXN][26],fail[MAXN],n,last;
    char s[MAXN];
public:
    void init(){
        scanf("%s",s+1);
        n = strlen(s+1);
        //0和1分别代表偶数长度回文串的根和奇数长度回文串的根
        len[0] = 0; len[1] = -1;
        fail[0] = 1; fail[1] = 0;
        tot = 1; last = 0;
    }
    int getfail(int x, int pos){
        while(s[pos]!=s[pos-len[x]-1]) x = fail[x];
        return x;
    }
    void insert(int x, int pos){
        int u = getfail(x,pos);
        int c = s[pos] - 'a';
        if(!ch[u][c]){
            len[++tot] = len[u] + 2;
            fail[tot] = ch[getfail(fail[u],pos)][c];
            sz[tot] = sz[fail[tot]] + 1;
            ch[u][c] = tot;
        }
        last = ch[u][c];
    }
    void build(){
        int k = 0;
        for(int i = 1; i <= n; i++){
            s[i] = (s[i]-97+k)%26+97;
            insert(last,i);
            printf("%d ",k = sz[last]);
        }
    }
}pam;
int main(){
    pam.init();
    pam.build();
    return 0;
}