并不对劲的loj3123:p5404[CTS2019]重复

题目大意

给一个小写字母串(s),问有多少个长度为(m)的小写字母串满足无限重复后存在一个子串的字典序小于(s)
(mleq 2000;nleq 2000;)

题解

“给出一些串,求以(或不以)这些串为子串的满足某些条件的串的数量”这类问题,通常是在AC自动机上dp。
这题也可以用同样的方法,但是题目中“无限重复后存在一个子串”比较难算,所以考虑反过来算:计算无限重复后不存在一个子串的字典序小于(s)的串数。
这可以先把字典序大于(s)的串建成AC自动机,再在这个AC自动机中找长度为(m)的约数的环。
可以先把(s)建成AC自动机,然后发现对于AC自动机中一点,它接受的某一位字符时,如果这个字符大于这个点最大的出边,这个子串的字典序一定大于(s),可以直接走到0。
如果这个字符小于这个点最大的出边,这个子串的字典序一定小于(s)
所以可以只记AC自动机中每个点最大的出边权值和它指向的点(该点能走到0的边数=26-最大边权)。
把AC自动机中的环分为两类:过0的和不过0的。
不过0的可以直接dfs:每个点只有一条不是走向0的出边,至多有1个环。注意如果环的长度为(m)的倍数,这个环代表了环的长度个串。
过0的:可以拆成(0走x步到某点的方案数 imes该点走m-x步到0的方案数),然后在AC自动机上dp。

代码
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define view(u,k) for(int k=fir[u];~k;k=nxt[k])
#define maxn 2007
#define LL long long
using namespace std;
void write(int x)
{
	if(x==0){putchar('0'),putchar('
');return;}
	int f=0;char ch[20];
	if(x<0)putchar('-'),x=-x;
	while(x)ch[++f]=x%10+'0',x/=10;
	while(f)putchar(ch[f--]);
	putchar('
');
	return;
}
const int mod=998244353;
int n,m,fa[maxn],to[maxn],w[maxn],f[maxn][maxn],g[maxn][maxn],vis[maxn],len,ans;
char s[maxn];
char gc(int x){return x+'a';}
int gx(char c){return c-'a';}
int mo(int x){return x>=mod?x-mod:x;}
int getr(int u)
{
	if(!u)return 0;
	if(vis[u]){return 1;}
	vis[u]=1;
	if(getr(to[u])){len++;return u!=to[u];}
	return 0;
}
int mul(int x,int y){int res=1;while(y){if(y&1)res=(LL)res*x%mod;x=(LL)x*x%mod,y>>=1;}return res;}
int main()
{
	scanf("%d%s",&m,s+1),n=strlen(s+1);
	rep(i,2,n)
	{
		fa[i]=fa[i-1];
		while(fa[i]&&s[fa[i]+1]!=s[i])fa[i]=fa[fa[i]];
		if(s[fa[i]+1]==s[i])fa[i]++;
	}
	rep(i,0,n)
	{
		dwn(j,25,0)
		{
			int p=i;if(i==n)p=fa[i];
			while(p&&s[p+1]!=gc(j))p=fa[p];
			if(s[p+1]==gc(j)){to[i]=p+1;w[i]=25-j;break;}
		}
	}
	getr(1);
	if(m%len==0)ans=len;
	f[0][0]=1;
	rep(i,1,m)
	{
		rep(j,0,n)
		{
			f[i][to[j]]=mo(f[i][to[j]]+f[i-1][j]);
			f[i][0]=mo(f[i][0]+(LL)f[i-1][j]*w[j]%mod);
		}
	}
	rep(i,0,n)g[1][i]=w[i];
	rep(i,2,m)rep(j,0,n)g[i][j]=g[i-1][to[j]];
	rep(i,0,m)rep(j,0,n)ans=mo(ans+(LL)f[i][j]*g[m-i][j]%mod);
	write(mo(mul(26,m)-ans+mod));
	return 0;
}
一些感想

还没清空任务栈就有膜您赛了咋办!

原文地址:https://www.cnblogs.com/xzyf/p/13046738.html