【国家集训队】 拉拉队排练(Manacher算法)

题目描述见原题。

分析

假如以s[i]为中心,求出的最长回文子串为len,那么可以分别得到一个长度为len-2,len-4,...,1的回文子串。若知道每种长度的回文串数量即可求出答案。

我们不妨这么做:先对每个点求出以它为中心,向左右拓展的最长回文串长度len,并把len的计数加一,然后把一点的回文串计数也加一。如果我们先计算出了每个点左右拓展的最长回文串,那么只需逆推,就可得到所有长度回文串的计数。然后只需对于每种可以选的长度做快速幂即可。注意此题只需要奇数长度的回文串。

求最长回文子串?当然是Manacher!模板都几乎不用改的。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef unsigned long long LL;
const int MAXN=1000005;
const LL mod=19930726;
char s[MAXN*2],a[MAXN];
int N=0,M,p[MAXN*2];
LL K,cnt[MAXN*2];

LL Pow(LL x,LL k,LL mo)
{
	LL s=1;
	while(k)
	{
		if(k&1) s=(s*x)%mo;
		x=x*x%mo;
		k=k>>1;
	}
	return s;
}

void solve()
{
	scanf("%d%lld%s",&M,&K,a);
	int i=0,maxright=0,mid=0;
	s[N]='$';s[++N]='#';
	while(i<M)
	{
		s[++N]=a[i];
		s[++N]='#';
		i++;
	}s[N+1]='\0';  //插入其他字符 
	for(i=1;i<N;i++)
	{
		p[i]=maxright>i?min(p[mid*2-i],maxright-i):1;
		while(s[i+p[i]]==s[i-p[i]]) p[i]++;
		if(i+p[i]-1>maxright)
		{
			maxright=i+p[i]-1;
			mid=i;
		}
		cnt[p[i]-1]++;  //第一遍统计 
	}
	for(i=M-(1-M%2);i>=1;i-=2) cnt[i]+=cnt[i+2];  //逆推统计 
	LL ans=1;
	for(LL i=M-(1-M%2);i>=1;i-=2)  //统计答案 
	{
		if(K<=cnt[i])
		{
			ans=(ans*Pow(i,K,mod))%mod;
			break;
		}
		ans=(ans*Pow(i,cnt[i],mod))%mod;
		K-=cnt[i];
	}
	cout<<ans;
}

int main()
{
	solve();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/WWWengine/article/details/81490076