[CF932G]Palindrome Partition(PAM回文划分dp)

题面

https://codeforces.com/problemset/problem/932/G

题解

前置知识

在做此题之前,需要了解利用PAM在\(O(n \log n)\)时间内解决最小回文划分、回文划分数等回文划分相关dp的方法(见上面链接)

有了这个基础,我们再做此题。由于"第一个与最后一个相同,第二个与倒数第二个相同……"这样的条件极难dp,所以我们考虑转化题意。

如何做呢?首先如果给出的字符串长度为奇数,那肯定是0啦。

否则的话,将字符串的前后半部分拆开,将后半部分翻转,再把它们“拉链式”合并。以本题样例1为例:

就成功地将原题那个恶心的条件变成了求回文串划分方案数。样例1的答案ab|cd|cd|ab对应着新序列的abba|cddc。

另外有一个地方与之前的回文划分dp不同,就是这里的回文划分中的每一个回文串长度必须是偶数。不过这个也好解决,dp时将所有奇数位的dp值都设成0就可以了。

代码

#include<bits/stdc++.h>

using namespace std;

#define ll long long
#define rg register
#define In inline

const int N = 1e6;
const ll mod = 1e9 + 7;

namespace ModCalc{
	In void Inc(ll &x,ll y){
		x += y;if(x >= mod)x -= mod;
	}
	In void Dec(ll &x,ll y){
		x -= y;if(x < 0)x += mod;
	}
	In ll Add(ll x,ll y){
		Inc(x,y);return x;
	}
	In ll Sub(ll x,ll y){
		Dec(x,y);return x;
	}
}
using namespace ModCalc;

char t[N+5],s[N+5];
int n;
ll f[N+5],g[N+5],slink[N+5],diff[N+5];

struct PAM{
	ll nx[N+5][26],len[N+5],fail[N+5];
	int last,cnt;
	void clear(){
		cnt++;
		len[1] = -1;
		fail[0] = fail[1] = 1;
	}
	void extend(char c,int n){
		int id = c - 'a';
		int p = last;
		while(s[n-len[p]-1] != s[n])p = fail[p];
		if(!nx[p][id]){
			cnt++;
		 	len[cnt] = len[p] + 2;
		 	int q = fail[p];
		 	while(s[n-len[q]-1] != s[n])q = fail[q];
		 	fail[cnt] = nx[q][id];
		 	nx[p][id] = cnt;
		 	diff[cnt] = len[cnt] - len[fail[cnt]];
		 	if(diff[cnt] != diff[fail[cnt]])
		 		slink[cnt] = fail[cnt];
	 		else
		 		slink[cnt] = slink[fail[cnt]];
		} 
		last = nx[p][id];
		p = last;
		while(p >= 2){
			g[p] = f[n-len[slink[p]]-diff[p]];
			if(diff[p] == diff[fail[p]])Inc(g[p],g[fail[p]]);
			if(!(n & 1))Inc(f[n],g[p]);
			p = slink[p];
		}
	}	
}P;

int main(){
	scanf("%s",t + 1);
	n = strlen(t + 1);
	if(n & 1){
		puts("0");return 0;
	}
	P.clear();
	ll _n = 0;
	for(rg int i = 1;(i << 1) <= n;i++)s[++_n] = t[i],s[++_n] = t[n+1-i];
	f[0] = 1;
	for(rg int i = 1;i <= n;i++)P.extend(s[i],i);
	cout<<f[n]<<endl;
	return 0;
}
posted @ 2020-10-04 20:03  coder66  阅读(167)  评论(0编辑  收藏  举报