CodeForces 932G Palindrome Partition(回文自动机 + dp)

CodeForces 932G Palindrome Partition(回文自动机 + dp)_第1张图片

 

 

大致题意:给你一个字符串,让你把它分为k个部分,k为偶数。设这k个部分分别表示为s1,s2,...,sk,问使得对于每一个i都满足S_i=S_{k-i+1}的划分方法有多少种,也即使得这k个部分构成回文的方法有多少种。

由于是回文,我们当然要想办法往回文上面去靠。根据题意有S_1=S_k,S_2=S_{k-1},...

我们假设第一段长度为j,令S_1=s_1s_2...s_j,S_k=s_{n-j+1},s_{n-j+2},...,s_n

s_1=s_{n-j+1},s_2=s_{n-j+2},...,s_{j-1}=s_{n-1},s_j=s_n

如果我们把排列方式换一下,令 S'=s_1s_ns_2s_{n-1}...s_{j-1}s_{n-j+2}s_{j}s_{n-j+1}

我们可以发现,S'恰好是一个回文串。

那么我们推广一下,我们不仅仅是把S1和Sk这么表示,我们把整个串这么表示。

也即,令S'=s_1s_ns_2s_{n-1}...s_{\frac{n}{2}-1}s_{n-\frac{n}{2}+2}s_{\frac{n}{2}}s_{n-\frac{n}{2}+1},最后发现问题变成了,求把S'分成偶数个回文串的方案数。

我们考虑这个问题,显然是可以建立一个回文自动机/回文树,然后做一个简单的dp。我们令dp[i]表示到S'的第i个字符时候的方案数,显然当i为奇数时dp[i]为0。状态转移方程也很容易得到,dp[i]=Σdp[j],要求子串s_{j+1}s_{j+2}...s_i是回文串。具体实现,在回文自动机上也是很容易实现。每次插入一个字符,对于对应的last节点,遍历它所有的fail指针指向的节点,这些节点的长度对应转移方程中的j。

然而,这个dp看似非常的简单,但是实际上并不能通过这道题目。注意到,每次我们需要遍历一个节点所有的fail指针指向的点。如果我们输入的字符串全部是一个字符,那么每次添加一个字符就会遍历之前加入的所有字符,也即算法退化成了平方的算法,对于百万的数据显然是会TLE的。

下面开始讨论一个关于回文串的一个很巧妙的性质。

之所以会出现退化,是因为关联的fail指针节点太多了,例如下面一组数据:

                                                                 ab

                                                             abab

                                                         ababab

                                                     abababab

                                                 ababababab

多观察几组数据,我们可以得出一个很显然的结论。

即如果一个字符串,设它的长度为len,那么它的所有长度大于len/2的fail指针节点构成一个等差数列。

这个结论反过来,如果该串有n个fail指针的节点,那么后n/2个串一定构成等差数列。

如果我们利用这个性质,把每一组等差数列和为一组一起求和计算,那么每次最多只有log个组,计算的代价也就是log。

具体来说,对于每一个串对应的位置,根据它与fail的长度差确定当前是否是等差数列的首项,如果不是则从fail那继承记录下来的首项。然后在dp的过程中,每次从当前位置直接往前跳到首项,每次往前跨越一个等差数列,跨越的同时统计贡献的前缀和。然后如果当前位置是偶数,那么更新dp数组。具体看代码吧,还是没有说的太清楚:

#include 
#define INF 0x3f3f3f3f
#define fi first
#define se second
#define LL long long
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%lld",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;

const int mod = 1e9 + 7;
const int N = 1000100;

int dp[N],f[N],nxt[N],d[N],len;
char s[N],ss[N];

struct Palindromic_Tree
{
    int tot,last;
    struct node{int ch[26],fa,len;} T[N];

    inline void init()
    {
        memset(T,0,sizeof(T));nxt[0]=1;
        T[tot=1].len=-1,T[0].fa=T[1].fa=1;
    }

    inline void ins(int c,int n,char *s)
    {
        int p=last;
        while(s[n-T[p].len-1]!=s[n])p=T[p].fa;
        if(!T[p].ch[c])
        {
            int v=++tot,k=T[p].fa;
            T[v].len=T[p].len+2;
            while(s[n-T[k].len-1]!=s[n])k=T[k].fa;
            T[v].fa=T[k].ch[c];T[p].ch[c]=v;
            d[v]=T[v].len-T[T[v].fa].len;
            nxt[v]=d[v]==d[T[v].fa]?nxt[T[v].fa]:T[v].fa;
        }
        last=T[p].ch[c];
    }

}PT;

int main()
{
    scanf("%s",s);
    len=strlen(s);
    if (len&1)
    {
        puts("0");
        return 0;
    }
    dp[0]=1;
    PT.init();
    for(int i=0;i

 

 

 

 

你可能感兴趣的:(回文自动机,---------Online,Judge--------,CodeForces,自动机dp)