回文树板子

大略:

回文树本质是一棵树,有两个根,奇根下面都是奇回文,偶根下面是偶回文,偶根\(fail\)指向奇根,到奇根一定适配。

  1. 求串\(S\)前缀\(0 - i\)内本质不同回文串的个数
  2. 求串\(S\)内每一个本质不同回文串出现的次数
  3. 求串\(S\)内回文串的个数(其实就是\(1\)\(2\)结合起来)
  4. 求以下标i结尾的回文串的个数

板子:

struct PAM{
    int nex[maxn][26];  //指向的一个字符的节点
    int fail[maxn]; //失配节点
    int len[maxn];  //当前节点回文长度
    int str[maxn];  //当前添加的字符串
    int cnt[maxn];  //节点出现次数
    int last;	//目前走到哪个节点
    int tot;    //PAM中节点数
    int N;  //添加的串的个数

    int newnode(int L){ //新建节点
        for(int i = 0; i < 26; i++) nex[tot][i] = 0;
        len[tot] = L;
        cnt[tot] = 0;
        return tot++;
    }

    void init(){
        tot = 0;
        newnode(0);
        newnode(-1);
        last = 0;
        N = 0;
        str[0] = -1;
        fail[0] = 1;	//偶根指向奇根,因为到奇根必匹配
    }

    int getfail(int x){ //失配
        while(N - len[x] - 1 < 0 || str[N - len[x] - 1] != str[N]) x = fail[x];
        return x;
    }

    void add(char ss){
        int c = ss - 'a';
        str[++N] = c;
        int cur = getfail(last);    //最长可扩增的后缀回文节点
        if(!nex[cur][c]){
            int now = newnode(len[cur] + 2);
            fail[now] = nex[getfail(fail[cur])][c];
            //cur后缀(除自己)的最长的能让now失配的后缀
            nex[cur][c] = now;
        }
        last = nex[cur][c];
        cnt[last]++;
    }

    void count(){
        for(int i = tot - 1; i >= 0; i--)   //子节点出现父节点也出现
            cnt[fail[i]] += cnt[i];
    }
}pam;
posted @ 2019-08-04 15:58  KirinSB  阅读(187)  评论(0编辑  收藏  举报