CF932G Palindrome Partition

思路

首先把字符串变为\(S[1]S[n]s[2]s[n-1] \dots\)
这样原来的一个合法的划分方案就变成了用k个长度为偶数的回文子串划分的方案,
然后直接DP,对i位置,可转移的位置就是它的几个回文后缀,在PAM上跳fail即可
但是复杂度是假的,一旦串的每个字符都相同,就需要跳\(O(n)\)次fail,总复杂度变成了\(O(n^2)\)

所以有这样一个性质,对一个节点x,它的所有fail的len最多是log个等差数列,因为对于长度大于\(\frac{len}{2}\)的情况,由于回文树的性质,长度一定是一个等差数列,每次len/2,所以有log段

考虑直接对这log端的贡献转移,在当前的位置i,设之前三个位置为a1,a2,a3(a1>a2>a3),由于回文串的性质S[i-a1,i-d]=S[i-a2,i],S[i-a2,i-d]=S[i-a3,i],所以a2,a3在i-d处已经被统计过了,加上i-a1的贡献即可

复杂度\(O(n\log n)\)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MOD = 1e9+7;
int trans[1000100][26],fail[1000100],Nodecnt,len[1000100],last,n,dp[1000100],pre[1000100],f[1000100],inc[1000100];
char mids[1000100],s[1000100];
int New_state(int _len){
    len[Nodecnt]=_len;
    return Nodecnt++;
}
int get_fail(int p,int n){
    while(mids[n-len[p]-1]!=mids[n])
        p=fail[p];
    return p;
}
void insert(int n){
    int cur=get_fail(last,n);
    if(!trans[cur][mids[n]]){
        int t=New_state(len[cur]+2);
        fail[t]=trans[get_fail(fail[cur],n)][mids[n]];
        trans[cur][mids[n]]=t;
        inc[t]=len[t]-len[fail[t]];
        if(inc[t]==inc[fail[t]])
            pre[t]=pre[fail[t]];
        else
            pre[t]=fail[t];
    }
    last=trans[cur][mids[n]];
}   
int main(){
    mids[0]=-1;
    New_state(0);
    fail[0]=1;
    New_state(-1);
    last=0;
    scanf("%s",s+1);
    n=strlen(s+1);
    // printf("n=%d\n",n);
    dp[0]=1;
    int inq=0;
    for(int i=1;i<=n/2;i++){
        mids[++inq]=s[i]-'a';
        insert(inq);
        for(int j=last;j;j=pre[j]){
            f[j]=dp[inq-len[pre[j]]-inc[j]];
            if(pre[j]!=fail[j])
                f[j]=(f[j]+f[fail[j]])%MOD;
            if(!(inq&1))
                dp[inq]=(dp[inq]+f[j])%MOD;
        }
        mids[++inq]=s[n-i+1]-'a';
        insert(inq);
        for(int j=last;j;j=pre[j]){
            f[j]=dp[inq-len[pre[j]]-inc[j]];
            if(pre[j]!=fail[j])
                f[j]=(f[j]+f[fail[j]])%MOD;
            if(!(inq&1))
                dp[inq]=(dp[inq]+f[j])%MOD;
        }
    }
    // for(int i=1;i<=n;i++)
    //     putchar(mids[i]+'a');
    // putchar('\n');
    printf("%d\n",dp[n]);   
    return 0;
}
posted @ 2019-04-22 07:24  dreagonm  阅读(315)  评论(0编辑  收藏  举报