• 【CF932G】Palindrome Partition 回文自动机


    【CF932G】Palindrome Partition

    题意:给你一个字符串s,问你有多少种方式,可以将s分割成k个子串,设k个子串是$x_1x_2...x_k$,满足$x_1=x_k,x_2=x_{k-1}...x_i=x{k-i+1}$。

    $|s|le 10^6$

    题解:设字符串的长度为n,考虑字符串$T=s_1s_ns_2s_{n-1}...$。问题就转化成了:求将原串划分成若干个长度为偶数的回文子串的方案数。

    首先我们有一种暴力的想法,设f[i]表示将前i个字符分成若干个回文子串的方案数,先跑回文自动机或Manacher得到所有$O(n^2)$个回文串。然后有转移方程$f[i]=sum f[j] [s(i,j)是回文串]$,特别地,对于奇数的i,f[i]=0。

    但是观察发现,如果$s(1,i)=A+DD..DT$,其中$T,DT,DDT...$都是回文串,那么显然有转移$f[i]=f[i-|T|]+f[i-|DT|]+...$,而注意到对于$i-|T|$这个位置,也存在转移$f[i-|T|]=f[i-|DT|]+f[i-|DDT|]+...$。也就是说,f[i]和f[i-|T|]有相当多的转移是相同的!我们能不能利用这个性质来优化我们的算法呢?

    接着,对于回文自动机上的每个节点,我们定义$diff[i]=len[i]-len[fail[i]]$,$sf[i]$表示i沿着fail指针往上走,走到的第一个$diff$与i不同的祖先。我们是不是可以把fail链上连续的diff相同的点放在一起呢?当然是可以的,有如下结论:

    引理1:从一个点沿着sf指针向上走,最多走log步就能到达根节点。

    证明很复杂,不过有一种比较容易的感性理解方法:就是当diff<len/2的时候,diff一定不变;diff>len/2的时候,diff又一定改变(画画图能看出来)。所以每次len缩小一半,长度就是log的了。

    为了保证正确性,我们还需要证明一个东西:

    引理2:设j是i对应的节点在fail树上的一个祖先,且diff[j]!=diff[fail[j]],则在所有以i-diff[j]结尾的回文串中,长度最长的为len[j]-diff[j]。

    这个也不太会证,画画图吧~

    不过引理2保证了我们直接调用f[i-diff[j]]时不会统计到无关的信息。现在我们可以将diff相同的部分放到一起处理了,只需要维护一个类似于前缀和的东西,到时候暴力沿着sf向上走统计答案即可。具体可见代码。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int P=1000000007;
    const int maxn=1000010;
    inline void upd(int &x,const int &y) {x+=y;	if(x>=P)	x-=P;}
    int f[maxn],g[maxn];
    int n,last,tot;
    int ch[maxn][26],fa[maxn],len[maxn],diff[maxn],sf[maxn];
    char ss[maxn],str[maxn];
    inline void extend(int x)
    {
    	int p=last,c=str[x]-'a';
    	for(;str[x-len[p]-1]!=str[x];p=fa[p]);
    	if(!ch[p][c])
    	{
    		int np=++tot,q=fa[p];	len[np]=len[p]+2;
    		for(;str[x-len[q]-1]!=str[x];q=fa[q]);
    		fa[np]=ch[q][c];
    		ch[p][c]=np;
    		diff[np]=len[np]-len[fa[np]];
    		if(diff[np]==diff[fa[np]])	sf[np]=sf[fa[np]];
    		else	sf[np]=fa[np];
    	}
    	last=ch[p][c];
    }
    int main()
    {
    	scanf("%s",ss+1),n=strlen(ss+1);
    	int i,p;
    	for(i=1;i+i<=n;i++)	str[i*2-1]=ss[i],str[i*2]=ss[n+1-i];
    	tot=fa[0]=1,len[1]=-1;
    	f[0]=1;
    	for(i=1;i<=n;i++)
    	{
    		extend(i);
    		p=last;
    		for(p=last;p>1;p=sf[p])
    		{
    			g[p]=f[i-len[sf[p]]-diff[p]];
    			if(diff[p]==diff[fa[p]])	upd(g[p],g[fa[p]]);
    			upd(f[i],g[p]);
    		}
    		if(i&1)	f[i]=0;
    	}
    	printf("%d",f[n]);
    	return 0;
    }
  • 相关阅读:
    「CF505E」 Mr. Kitayuta vs. Bamboos
    「CF1438D」 Powerful Ksenia
    Kruskal重构树
    20210528模拟赛总结
    20210527模拟赛总结
    20210526模拟赛总结
    20210525模拟赛总结
    CF #722 Div2题解
    洛谷P3652 csh和zzy的战争 题解
    [清华集训2012]模积和 题解
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/8468783.html
Copyright © 2020-2023  润新知