Codeforces 932G Palindrome Partition

在这里插入图片描述

题意

给一个串,划分成k个部分,使得这k个部分整体是回文

题解

转化为 s 1 s n s 2 s n − 1 . . . s n / 2 s n / 2 + 1 s_1s_ns_2s_{n-1}...s_{n/2}s_{n/2+1} s1sns2sn1...sn/2sn/2+1
变为划分为k个部分,每个部分内部是回文
d p dp dp 方程很好想, d p [ i ] = ∑ d p [ j ] ∗ [ s [ j + 1 , i ] 为 回 文 ] dp[i]=\sum dp[j]*[s[j+1,i]为回文] dp[i]=dp[j][s[j+1,i]]
朴素的做法是暴力跳 f a i l fail fail,TLE,考虑优化
一个回文串的所有的后缀回文串可以组成不超过 l o g log log 个等差数列,所以,整个等差数列一起更新
设一个等差数列为 a i a i − 1 a i − 2 . . . a 1 ( l e n [ a i ] > l e n [ a i − 1 ] ) a_ia_{i-1}a_{i-2}...a_1(len[a_i]>len[a_{i-1} ]) aiai1ai2...a1(len[ai]>len[ai1])
g [ i ] g[i] g[i] 表示从 i 到 1 i到1 i1 的和,即 g [ i ] = f [ p o s − a i ] + f [ p o s − a i − 1 ] + . . . + f [ p o s − a 1 ] g[i]=f[pos-a_{i}]+f[pos-a_{i-1}]+...+f[pos-a_{1}] g[i]=f[posai]+f[posai1]+...+f[posa1]
那么, f [ p o s ] f[pos] f[pos] 就等于所有的等差数列的首相对应的 g [ i ] g[i] g[i] 的和
下面考虑如何维护 g [ i ] g[i] g[i]
在这里插入图片描述
已知 g [ 3 ] g[3] g[3],考虑如何得出 g [ 4 ] g[4] g[4] ,发现
    g [ 4 ] = f [ k − 3 ′ ] + f [ k − 2 ′ ] + f [ k − 1 ′ ] + f [ k − 1 ′ ′ ] ~~~g[4]=f[k-3']+f[k-2']+f[k-1']+f[k-1'']    g[4]=f[k3]+f[k2]+f[k1]+f[k1]
g [ 3 ] = f [ k − 3 ] + f [ k − 2 ] + f [ k − 1 ]             = f [ k − 3 ′ ] + f [ k − 2 ′ ] + f [ k − 1 ′ ] g[3]=f[k-3]+f[k-2]+f[k-1]\\~~~~~~~~~~~=f[k-3']+f[k-2']+f[k-1'] g[3]=f[k3]+f[k2]+f[k1]           =f[k3]+f[k2]+f[k1]
其中,根据 border 理论(这个不明白的话自己学一下), 1 ′ ′ 和 1 ′ 1''和1' 11 是完全相同的
所以, g [ 4 ] g[4] g[4] g [ 3 ] g[3] g[3] 的基础上,再加上 f [ k − 1 ′ ′ ] f[k-1''] f[k1] 即可
进一步,可以发现,其实就是每次加上数列的末项!!!
u p [ j ] up[j] up[j] 表示结点 j j j 所在数列的末项
f [ k − l e n [ u p [ j ] ] f[k-len[up[j]] f[klen[up[j]] 就是 f [ k − 1 ′ ′ ] f[k-1''] f[k1]
然后在加上 g [ f a i l [ j ] ] g[fail[j]] g[fail[j]] 就是更新后的 g [ j ] g[j] g[j] 了,累加到 f [ k ] f[k] f[k] 即可

吐槽:

为什么别人的博客写的那么晦涩,最关键的 g [ j ] g[j] g[j]的更新根本看不懂
全部写成 f [ k − (   l e n [ f a i l [ u p [ j ]   ] + d i f f [ j ]   )   ] f[k-(~len[fail[up[j]~]+diff[j]~)~] f[k( len[fail[up[j] ]+diff[j] ) ] 根本就是个坑
算了,还是自己太弱了

代码
#include<bits/stdc++.h>
#define N 1000010
#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=0;
LL f[N],g[N];
struct PAM {
    int next[N][26] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
    int fail[N] ;//fail指针,失配后跳转到fail指针指向的节点
    int len[N] ;//len[i]表示节点i表示的回文串的长度
    int s[N] ;//存放添加的字符
    int last ;//指向上一个字符所在的节点,方便下一次add
    int n ;//字符数组指针
    int p ;//节点指针
    int d[N],up[N],id[N];
    inline int newnode ( int l ) {//新建节点
        for ( int i = 0 ; i < 26 ; ++ i ) next[p][i] = 0 ;
        len[p] = l ;
        return p ++ ;
    }
    inline void init () {//初始化
        p = 0 ;
        newnode (  0 ) ;
        newnode ( -1 ) ;
        last = 0 ;
        n = 0 ;
        s[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
        fail[0] = 1 ;
    }
    inline int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
        while ( s[n - len[x] - 1] != s[n] ) x = fail[x] ;
        return x ;
    }
    inline void add ( int c,int w ) {
        c -= 'a' ;
        s[++ n] = c ;
        int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
        if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
            int now = newnode ( len[cur] + 2 ) ;//新建节点
            fail[now] = next[get_fail ( fail[cur] )][c] ; 
            next[cur][c] = now ;
            d[now]=len[now]-len[fail[now]];
            up[now]=(d[fail[now]]==d[now]?up[fail[now]]:now);
        }
        last = next[cur][c] ;
        id[w]=last;
    }
    void slove(){
        for(int i=1;i<=n;i++)
            for(int j=id[i];j;j=fail[up[j]]){//遍历所有i为结尾的回文串,按照等差整块遍历,最多log次
                g[j]=f[i-len[up[j]]];
                if (d[j]==d[fail[j]]) g[j]=(g[j]+g[fail[j]])%mod;
                if (!(i&1)) f[i]=(f[i]+g[j])%mod;
            }
    }
}A;

char s[N];
char ss[N];
int main(int argc, char const *argv[]){
    scanf("%s",s+1);
    int n=strlen(s+1);
    for(int i=1;i<=n;i+=2) ss[i]=s[i/2+1];
    for(int i=2;i<=n;i+=2) ss[i]=s[n-i/2+1];
    A.init();   
    for(int i=1;i<=n;i++) A.add(ss[i],i);
    f[0]=1;
    A.slove();  
    printf("%lld\n",f[n]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值