题意

一开始一个空串,4中操作
1.首部添加一个字符
2.尾部添加一个字符
3.询问当前串的回文串种类数
4.询问当前串的回文串个数

题解

对回文树进行改造
对于指向串尾的指针,这时需要两个变量维护 L , R L,R L,R ,从中间位置开始先两侧扩展
同理,指向上一个回文串结点的 l a s t last last 也需要变为两个 l a s t [ 0 ] , l a s t [ 1 ] last[0],last[1] last[0],last[1]
还有一个注意的地方为,边界 1 -1 1,朴素的回文树是,让 0 的位置为 -1 ,改造以后
若是向尾部增加字符,则 s [ L 1 ] s[L-1] s[L1] 的位置要变为 -1 ,反之, s [ R + 1 ] s[R+1] s[R+1] 的位置要变为 -1
最后注意的是,若更新完之后,发现当前结点表示的串就是整个字符串,则两个 l a s t last last 要指向同一个结点

真的会加深对回文树的理解 😛

代码

#include<bits/stdc++.h>
#define N 200010
#define INF 0x3f3f3f3f
#define eps 1e-6
#define pi 3.141592653589793
#define mod 1000000007
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<" : "<<x<<endl
#define mem(x,y) memset(x,0,sizeof(int)*(y+3))
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef  pair<int,int> pp;
LL ans;
char s[N];
struct PAM {
    int next[N][26] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
    int fail[N] ;//fail指针,失配后跳转到fail指针指向的节点
    int cnt[N]; //表示节点i表示的回文串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)
    int num[N] ; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数(包括本身)。
    int len[N] ;//len[i]表示节点i表示的回文串的长度
    int s[N] ;//存放添加的字符
    int pos[N];
    int last[2] ;//指向上一个字符所在的节点,方便下一次add
    // int n ;//字符数组指针
    int p ;//节点指针
    //r为结尾的回文串的长度一定可以分成logn段等差数列
    int L,R=0;
    inline int newnode ( int l ) {//新建节点
        for ( int i = 0 ; i < 26 ; ++ i ) next[p][i] = 0 ;
        cnt[p]= 0 ;
        num[p] = 0 ;
        len[p] = l ;
        return p ++ ;
    }
    inline void init (int x) {//初始化
        p = 0 ;
        newnode (  0 ) ;
        newnode ( -1 ) ;
        last[0]=last[1] = 0 ;
        L=x+1;
        R=x;
        s[L]=s[R]=-1;//开头放一个字符集中没有的字符,减少特判
        fail[0] = 1 ;
        mem(num,x*2);
    }
    inline int get_fail ( int x,int d ) {//和KMP一样,失配后找一个尽量最长的
        if (!d)
            {s[R+1]=-1;while(s[L+len[x]+1]!=s[L]) x=fail[x];}
        else
            {s[L-1]=-1;while ( s[R - len[x] - 1] != s[R] ) x = fail[x] ;}
        return x ;
    }
    inline int add ( int c ,int d ) {
        c -= 'a' ;
        if(!d)
            s[--L]=c;
        else
            s[++R]=c;
        int cur = get_fail ( last[d],d ) ;//通过上一个回文串找这个回文串的匹配位置
        if (!next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
            int now = newnode ( len[cur] + 2 ) ;//新建节点
            fail[now] = next[get_fail ( fail[cur],d )][c] ; 
            next[cur][c] = now ;
            num[now] = num[fail[now]] + 1 ;
        }
        last[d] = next[cur][c] ;
        cnt[last[d]] ++ ;
        if (len[last[d]]==R-L+1) last[d^1]=last[d];
        // pos[cc]=last;
        return num[last[d]];
    }
    //注意add的返回值,若为num[last]表示s[1…i]的所有后缀的回文串的个数
    void count () {         //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
        for(int i = p-1; i >= 0; i--)cnt[fail[i]] += cnt[i];
    }
}A;


int main(int argc, char const *argv[]){
    int n;
    while(~sc(n)){
        A.init(n);
        LL sum=0;
        while(n--){
            int op; char c[3];
            sc(op);
            if (op==1){
                scanf("%s",c);
                sum+=A.add(c[0],0);
            }else
            if (op==2){
                scanf("%s",c);
                sum+=A.add(c[0],1);
            }else
            if (op==3){
                printf("%d\n",A.p-2);
            }else
            if (op==4){
                printf("%lld\n",sum);
            }
        }
    }
    return 0;
}