#回文自动机#CF932G Palindrome Partition

题目

给定一个串,把串分为偶数段,假设分为了 \(s_1,s_2,s_3,\dots,s_k\),求满足 \(s_1=s_k,s_2=s_{k-1},\dots\) 的方案数


分析

将原串变成 \(s_1s_ns_2s_{n-1}s_3s_{n-2}\dots\) 的形式,就转化为将字符串划分为若干个偶回文串的方案数。

维护回文树,暴力跳 fail 显然不可以,而 \(s\) 的所有回文后缀按照长度排序后,可以划分成 \(\log |s|\) 段等差数列。

回文树上的每个节点 \(u\) 需要多维护两个信息,\(diff[u]\)\(slink[u]\)\(diff[u]\) 表示节点 \(u\)\(fail[u]\) 所代表的回文串的长度差,即 \(len[u]-len[fail[u]]\)

\(slink[u]\) 表示 \(u\) 一直沿着 \(fail\) 向上跳到第一个节点 \(v\),使得 \(diff[v] \neq diff[u]\),也就是 \(u\) 所在等差数列中长度最小的那个节点。

\(g[v]\) 表示 \(v\) 所在等差数列的 \(dp\) 值之和,且 \(v\) 是这个等差数列中长度最长的节点,则 \(g[v]=\sum_{slink[x]=slink[v]} dp[i-len[x]]\),这里 \(i\) 是当前枚举到的下标。

因此,\(g[p]=g[fail[p]]+dp[i-len[slink[p]]-diff[p]]\),加上 \(fail[p]\) 当且仅当是同一个等差数列,这样就维护好了,\(p\)\(slink\) 链求出 \(dp[i]=\sum g[p]\)

时间复杂度 \(O(n\log n)\)\(\log\) 来自跳 \(slink\) 的时间。


代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1000011,mod=1000000007;
char s[N],str[N]; int n,dp[N],f[N];
struct PAM{
    int trie[N][26],fail[N],len[N],dep[N],diff[N],slink[N],lst,cnt;
    void BUILD(){fail[0]=cnt=1,len[1]=-1;}
    int getf(int n,int now){for (;s[n-len[now]-1]^s[n];now=fail[now]); return now;}
    void Insert(int n){
        int Lst=getf(n,lst);
        if (!trie[Lst][s[n]-97]){
            int now=++cnt;
            len[now]=len[Lst]+2;
            fail[now]=trie[getf(n,fail[Lst])][s[n]-97];
            trie[Lst][s[n]-97]=now;
			diff[now]=len[now]-len[fail[now]];
            if (diff[now]==diff[fail[now]]) slink[now]=slink[fail[now]];
                else slink[now]=fail[now];
        }
        lst=trie[Lst][s[n]-97];
    }
}pam;
void Mo(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
int main(){
	pam.BUILD(),scanf("%s",str+1);
	n=strlen(str+1),dp[0]=1;
    for (int i=1;i<=n;++i)
    if (i&1) s[i]=str[(i+1)>>1];
        else s[i]=str[n-(i>>1)+1];
    for (int i=1;i<=n;++i){
        pam.Insert(i);
        for (int p=pam.lst;p;p=pam.slink[p]){
            f[p]=dp[i-pam.len[pam.slink[p]]-pam.diff[p]];
            if (pam.slink[p]!=pam.fail[p]) Mo(f[p],f[pam.fail[p]]);
            if (!(i&1)) Mo(dp[i],f[p]);
        }
    }
    return !printf("%d",dp[n]);
}
posted @ 2025-06-10 06:23  lemondinosaur  阅读(5)  评论(0)    收藏  举报