题解 CF932G【Palindrome Partition】

$\text{Link}$

题意

给出字符串 $A$ ,求 $A=A_1A_2A_3...A_k$ 的划分满足 $2|k$ 且 $\forall i\in[1,k],A_i=A_{k-i+1}$,对 $10^9+7$ 取模。

$|A|\le 10^6$.

思路

回文划分的模板题,我觉得 $\text{zhylj}$ 的题解写得很好,我就简述一下吧。


我们知道,字符串的 $\text{border}$ 集合可以划分为 $O(\log n)$ 个等差数列。

考虑一个回文串的回文后缀:

设 $t=s_{k,...n}$,若 $k,t\text{ is palindromic}$,则 $t\text{ is a border of } k$。

证:

因为 $k\text{ is palindromic}$,所以 $s_i=s_{|s|-i+1}$;

因为 $t\text{ is palindromic}$,所以$s_i=s_{|s|-i+1}=s_{|s|-(|t|-i+1)+1}=s_{|s|-|t|+1}$;

所以 $t\text{ is a border of } k$。

即得到回文串的回文后缀集合可以划分为 $O(\log n)$ 个等差数列。


在 $\text{PAM}$ 上,记 $diff_x=len_x-len_{fail_x}$,$slink_x$ 表示 $\text{Fail}$ 树上 $x$ 点祖先中离 $x$ 最近的满足 $diff_{a}\ne diff_x$ 的 $a$。

容易发现,在 $x\to slink_x$ 的路径中,$len_x-len_{fail_x}$ 恒为定值,即在跳一条等差数列,由上面的结论得,$x$ 最多能跳 $O(\log n)$ 次就会达到根节点。


首先考虑给出字符串 $A$ ,求 $A=A_1A_2A_3...A_k$ 的划分数满足 $\forall i\in[1,k],A_i\text{ is palindromic}$ 应该怎么做。

我们考虑一个 $\text{dp}$,设 $dp_i$ 表示 $i$ 前缀的答案,则有一个显然的转移方程:

$$dp_i=\sum_{j,s_{j+1,...,i\text{ is palindromic}}}dp_j$$ 如果枚举 $j$ 只能做到 $O(n^2)$ 的时间复杂度。

由于回文串的回文后缀又是 $\text{border}$,所以等差数列中的后缀都平移了公差 $diff_x$。

那么 $dp$ 就成为了 $dp_{i-len_{slink_x}-diff_x}$,于是我们可以记录 $f_x$ 表示 $x\to slink_x$ 中链上回文子串最后一次出现对应的 $dp$ 值,我们可以简单地将 $f_{fail_x}$ 转移至 $f_x$,于是在加入节点时我们就可以跳 $x\to slink_x$,更新 $f_x$ 即可。

然后回到本题,我们可以考虑构造 $B=a_1a_na_2a_{n-1}...$,问题转化为对 $B$ 的偶回文子串划分。

那么只在 $2|i$ 时做出贡献即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=1e6+10,mod=1e9+7;
int n;
int dp[N],f[N];
char str[N],inp[N];
struct Palindromic_Tree{
    struct node{
        int ch[26];
        int fail,len,diff,slink;
    }a[N];
    int last,siz;
    inline void init(){
        siz=1,last=0;
        a[0].fail=1,a[1].fail=0;
        a[0].len=0,a[1].len=-1;
    }
    inline int getfail(int p,int x){
        while(str[x]!=str[x-a[p].len-1])
            p=a[p].fail;
        return p;
    }
    inline void insert(char ch,int x){
        int p=getfail(last,x);
        int w=ch-'a';
        if(!a[p].ch[w]){
            int cur=++siz;
            a[cur].len=a[p].len+2;
            int tmp=getfail(a[p].fail,x);
            a[cur].fail=a[tmp].ch[w];
            a[p].ch[w]=cur;
            a[cur].diff=a[cur].len-a[a[cur].fail].len;
            a[cur].slink=(a[cur].diff==a[a[cur].fail].diff)?a[a[cur].fail].slink:a[cur].fail;
        }
        last=a[p].ch[w];
    }
    inline void calc(){
        init();
        n=readstr(inp);
        if(n&1){
            write(0);
            return ;
        }
        for(int i=1;i<=n;i+=2) str[i]=inp[i+1>>1];
        reverse(inp+1,inp+n+1);
        for(int i=2;i<=n;i+=2) str[i]=inp[i+1>>1];
        dp[0]=1;
        str[0]='\n';
        for(int i=1;i<=n;i++){
            insert(str[i],i);
            for(int j=last;j;j=a[j].slink){
                f[j]=dp[i-a[a[j].slink].len-a[j].diff];
                if(a[j].slink!=a[j].fail){
                    f[j]=(f[j]+f[a[j].fail])%mod;
                }
                if(i%2==0)
                    dp[i]=(dp[i]+f[j])%mod;
            }
        }
        write(dp[n]);
    }
}PAM;
int main(){
    PAM.calc();
    flush();
    return 0;
} 

居然跑到了洛谷 rk1

再见 qwq~

posted @ 2021-07-01 11:38  ffffyc  阅读(6)  评论(0)    收藏  举报  来源