ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CodeForces - 932G Palindrome Partition

2021-01-13 23:34:29  阅读:10  来源: 互联网

标签:Palindrome text Partition CodeForces len fa now Block 回文


\(\text{Description}\)

传送门

\(\text{Solution}\)

有一个很强的转化:考虑构造 \(s'=s[1]s[n]s[2]s[n-1]...s[\frac{n}{2}]s[\frac{n}{2}+1]\)。具体就感性理解吧:由于题目要求 \(s_1=s_k,s_2=s_{k-1}...\),可以知道分段后一定是对称的,所以反转就是要求 \(s’\) 的回文串分割次数(注意这里的回文是偶回文)。

显然有这样的转移:

\[f[i]=\sum f[j] \]

其中 \([j+1,i]\) 是一个偶回文串,这个可以用后缀自动机跳 \(fa\) 来维护。

考虑时间复杂度,其实这是 \(\mathcal O(n^2)\) 的,数据是一个全为同种字符的串。

定义 \(\text{dif}(x)=s[x].len-s[s[x].fa].len\),\(\text{Block}(x)\) 是 \(x\) 往上跳 \(fa\) 时第一个 \(\text{dif}\) 不等于 \(\text{dif}(x)\) 的节点。再令 \(g[x]\) 是 \(x\) 往上跳 \(fa\) 到 \(\text{Block}(x)\)(不包含 \(\text{Block}(x)\))的节点的 \(f\)。

设 \(x\) 是加入 \(i\) 时的 \(now\)(\(now\) 可以看看代码)。则 \(g[x]\) 就是 \(f[i-s[x].len]+f[i-s[x].len+dif[x]]+f[i-s[x].len+dif[x]\times 2]+...+f[i-s[\text{Block}(x)].len-dif[x]]\)。

这样搞的原因是使 \(f[x]\) 可以由 \(f[s[x].fa]\) 转移过来。

嫖了一张图(\(\text{slink}\) 就是 \(\text{Block}\)):

显然从下往上数第一条橙线就是从下往上数第二条蓝线(即 \(s[x].fa\))。橙线与其上面蓝线一一对应。

显然根据上面的定义,\(s[x].fa\) 未统计 \(\text{Block}(x)\) 那条橙线,但 \(x\) 是能统计到 \(\text{Block}(x)\) 下面那条蓝线的(显然橙线与下面一条蓝线贡献的 \(f\) 值一样)。

所以 \(g[x]=g[s[x].fa]+f[i-s[\text{Block}(x)].len-\text{dif}(x)]\)。

完了?还没呢!最重要的是,如何证明时间复杂度。

设 \(x,y,z\) 都是回文串并且 \(y\) 是 \(x\) 的最长回文后缀,\(z\) 是 \(y\) 的最长回文后缀。那么有:

结论 1:如果 \(u,v\) 的长度相等,那么 \(u,v\) 是两个一模一样的字符串。

证明(约定 \(a>b\) 为长度比较):

  • \(u>y\)。\(v\) 和 \(u\) 的长度不可能相等。
  • \(u\le y\)。\(u\le y\)。由于 \(x\) 是回文串所以 \(uy=yu\) ,所以 \(v\) 是 \(x\) 的一个前缀,所以得证。

结论 2:不存在 \(u<v\) 的情况。

证明:

  • \(u>y\)。显然。
  • \(u\le y\)。此时因为 \(y\) 是 \(x\) 的最长回文后缀,\(z\) 是 \(y\) 的最长回文后缀,那么 \(u,v\) 相对就是满足 \(x,y\) 最短的。将表中第二行与第四行对比,发现 \(u\) 移到第二行成为 \(y\) 的前缀后有中间那一部分是回文串(\(x\) 是回文串,\(u=u\))。则 \(u\) 也可以满足 \(y\)。若 \(u<v\),\(v\) 就不是最小的了,矛盾。

结论 3:若 \(u>v\),则 \(u>z\)。

证明:

反证法。

假设 \(z>u\),且 \(u>v\)。

则 \(z\) 是 \(zu\) 的一个长度过半的公共前后缀。由于 \(z\) 是一个回文串,则此时 \(zu\) 是一个回文串(对比第一行和第五行)。又因为 \(u>v\),所以 \(zu>y\),且 \(zu\) 还是 \(x\) 的一个后缀,这与 \(y\) 是 \(x\) 的最长回文后缀的性质相矛盾。

回到原题,容易发现 \(u=v\) 时就是那一连串 \(\text{dif}\) 相等的情况,当 \(u>v\) 就是遇到了 \(\text{Block}(x)\)。由 结论 3 得 \(s[\text{Block}(x)].len<\frac{s[x].len}{2}\)!

所以每遇见 \(\text{Block}(x)\) 就会使长度减半,我们最多会遇见 \(\log n\) 个 \(\text{Block}(x)\)!

所以时间复杂度 \(\mathcal O(n\log n)\)。

\(\text{Code}\)

#include <cstdio>

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}

#include <cstring>

const int mod=1e9+7,maxn=1e6+5;

char ori[maxn],str[maxn];
int n,g[maxn],f[maxn],len;
struct PAM {
	int cnt,las;
	struct node {
		int fa,len,son[26],dif,Block;
	} s[maxn];
	PAM() {
		s[0].len=0,s[1].len=-1;
		s[0].fa=s[1].fa=1;
		las=0,cnt=1;
	}
	int GetFail(int x) {
		while(str[n-s[x].len-1]^str[n]) x=s[x].fa;
		return x;
	}
	void Extend(int c) {
		int cur=GetFail(las),now=s[cur].son[c];
		if(!now) {
			now=++cnt;
			s[now].len=s[cur].len+2;
			s[now].fa=s[GetFail(s[cur].fa)].son[c];
			s[cur].son[c]=now;
			s[now].dif=s[now].len-s[s[now].fa].len;
			s[now].Block=(s[now].dif^s[s[now].fa].dif)?s[now].fa:s[s[now].fa].Block;
		}
		las=now;
	}
	void Update(int i) {
		for(int p=las;p;p=s[p].Block) {
			g[p]=f[i-s[s[p].Block].len-s[p].dif];
			if(s[p].Block^s[p].fa) g[p]=(g[p]+g[s[p].fa])%mod; // 特判!!!
			if(!(i&1)) f[i]=(f[i]+g[p])%mod;
		}
	}
} mc;

int main() {
	scanf("%s",ori+1); len=strlen(ori+1);
	rep(i,1,len>>1) str[(i<<1)-1]=ori[i];
	rep(i,(len>>1)+1,len) str[(len-i+1)<<1]=ori[i];
	f[0]=1;
	rep(i,1,len) {
		++n;
		mc.Extend(str[i]-'a');
		mc.Update(i);
	}
	print(f[n],'\n');
	return 0;
}

\(\text{Reference}\)

\(\mathtt{crashed}\)

\(\mathtt{shadowice1984}\)

标签:Palindrome,text,Partition,CodeForces,len,fa,now,Block,回文
来源: https://www.cnblogs.com/AWhiteWall/p/14274777.html

专注分享技术,共同学习,共同进步。侵权联系[admin#icode9.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有