[LOJ3123][KMP][DP]CTS2019:重复

LOJ3123

首先容斥一下,统计不合法方案
对S建出KMP自动机,则答案显然是走m步走出一个环的方案,枚举起点pos,直接dp可以获得60分
注意到一个点指向非根节点的出边是唯一的,不然就无法满足字典序的要求,所以可以枚举走到根节点的时刻再dp
预处理dp数组 f [ i ] [ j ] f[i][j] f[i][j] i i i步走到 j j j的方案数

Code:

#include
#define ll long long
#define mod 998244353
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
inline int add(int a,int b){a+=b;if(a>=mod) a-=mod;return a;}
inline int dec(int a,int b){a-=b;if(a<0) a+=mod;return a;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline void inc(int &a,int b){a+=b;if(a>=mod) a-=mod;}
inline int ksm(int a,int b){int res=1;for(;b;b>>=1,a=mul(a,a)) if(b&1) res=mul(res,a);return res;}
const int N=2e3+5;
int n,m,nxt[N],pos[N],dp[N][N]; 
char s[N];
inline void kmp(){
	pos[0]=1;
	for(int i=1;i<=n;i++){
		int j=nxt[i-1];
		while(j && s[j+1]!=s[i]) j=nxt[j];
		if(s[j+1]==s[i] && i!=1) ++j;
		nxt[i]=j;pos[i]=pos[j];
		if(s[i+1]>=s[pos[j]]) pos[i]=i+1;
	}
}
inline void Dp(){
	dp[0][0]=1;
	for(int i=1;i<=m;i++)
		for(int j=0;j<=n;j++){
			inc(dp[i][pos[j]],dp[i-1][j]);
			inc(dp[i][0],mul(dp[i-1][j],('z'-s[pos[j]])));
		}
}
inline int get_ans(){
	int res=0;
	for(int i=0;i<=n;i++){
		int p=i;
		for(int j=0;j<m;j++){
			if(p==0){inc(res,dp[m-j][i]);break;}
			inc(res,mul(dp[m-j-1][i],'z'-s[pos[p]]));
			p=pos[p];
			if(j==m-1 && p==i) inc(res,1);
		}
	}
	return res;
}
int main(){
	m=read();scanf("%s",s+1);n=strlen(s+1);
	kmp();Dp();
	cout<<dec(ksm(26,m),get_ans());
	return 0;
}

你可能感兴趣的:(KMP,-----DP-----,-----字符串-----)