题解 CF932G【Palindrome Partition】

Link

题意

给出字符串 A ,求 A=A1A2A3...Ak 的划分满足 2|ki[1,k],Ai=Aki+1,对 109+7 取模。

|A|106.

思路

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


我们知道,字符串的 border 集合可以划分为 O(logn) 个等差数列。

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

t=sk,...n,若 k,t is palindromic,则 t is a border of k

证:

因为 k is palindromic,所以 si=s|s|i+1;

因为 t is palindromic,所以si=s|s|i+1=s|s|(|t|i+1)+1=s|s||t|+1

所以 t is a border of k

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


PAM 上,记 diffx=lenxlenfailxslinkx 表示 Fail 树上 x 点祖先中离 x 最近的满足 diffadiffxa

容易发现,在 xslinkx 的路径中,lenxlenfailx 恒为定值,即在跳一条等差数列,由上面的结论得,x 最多能跳 O(logn) 次就会达到根节点。


首先考虑给出字符串 A ,求 A=A1A2A3...Ak 的划分数满足 i[1,k],Ai is palindromic 应该怎么做。

我们考虑一个 dp,设 dpi 表示 i 前缀的答案,则有一个显然的转移方程:

dpi=j,sj+1,...,i is palindromicdpj 如果枚举 j 只能做到 O(n2) 的时间复杂度。

由于回文串的回文后缀又是 border,所以等差数列中的后缀都平移了公差 diffx

那么 dp 就成为了 dpilenslinkxdiffx,于是我们可以记录 fx 表示 xslinkx 中链上回文子串最后一次出现对应的 dp 值,我们可以简单地将 ffailx 转移至 fx,于是在加入节点时我们就可以跳 xslinkx,更新 fx 即可。

然后回到本题,我们可以考虑构造 B=a1ana2an1...,问题转化为对 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 @   ffffyc  阅读(1)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示