【CF585F】Digits of Number Pi(AC自动机上数位DP)

点此看题面

  • 给定一个长为(n)的字符串(s)和两个长为(d)的字符串(l,r)
  • 问有多少长为(d)的在([l,r])内的字符串,满足存在长度至少为(lfloorfrac d2 floor)的子串是(s)的子串。
  • (nle10^3,dle50)

(AC)自动机上数位(DP)

如果我们把(s)每个长度为(lfloorfrac d2 floor)的子串都取出来建一个(AC)自动机,那么我们就是要求有多少个([l,r])内的字符串能匹配到至少一个模式串。

于是就设(f_{x,rt,0/1,0/1})表示做到第(x)个字符、对应(AC)自动机上编号为(rt)的节点、是否完成过匹配、是否卡到数字上界的方案数。

转移就枚举一下填什么字符,还是比较简单的一题。

代码:(O(10nd^2))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000
#define D 50
#define X 1000000007
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,d;char s[N+5],l[D+5],r[D+5];
namespace AcAutomation//AC自动机
{
	int Nt=1;struct node {int P,F,S[30];}O[N*D+5];
	I void Ins(CI x,CI y)//插入子串[l,r]
	{
		RI t,k=1;for(RI i=x;i<=y;k=O[k].S[t],++i)
			!O[k].S[t=s[i]&15]&&(O[k].S[t]=++Nt);O[k].P=1;//给结尾打上标记
	}
	int q[N*D+5];I void Build()//建AC自动机
	{
		RI i,k,H=1,T=1;for(i=0;i<=9;++i)
			(O[1].S[i]?O[q[++T]=O[1].S[i]].F:O[1].S[i])=1;
		W(H<=T) for(k=q[H++],i=0;i<=9;++i)
			(O[k].S[i]?O[q[++T]=O[k].S[i]].F:O[k].S[i])=O[O[k].F].S[i];
	}
	int f[D+5][N*D+5][2];I int DP(char *w,CI x=1,CI rt=1,CI p=0,CI fg=0)//数位DP
	{
		if(x>d) return p;if(fg&&~f[x][rt][p]) return f[x][rt][p];//边界;记忆化
		RI i,k,t=0,lim=fg?9:(w[x]&15);for(i=0;i<=lim;++i)//枚举填谁
			k=O[rt].S[i],Inc(t,DP(w,x+1,k,p||O[k].P,fg||i^lim));//更新状态
		return fg&&(f[x][rt][p]=t),t;//记忆化
	}
}
int main()
{
	using namespace AcAutomation;memset(f,-1,sizeof(f));
	RI i;scanf("%s%s%s",s+1,l+1,r+1),n=strlen(s+1),d=strlen(l+1);
	for(i=1;i<=n-(d>>1)+1;++i) Ins(i,i+(d>>1)-1);Build();//取出所有长d/2的子串建AC自动机
	for(i=d;l[i]=='0';--i) l[i]='9';--l[i];return printf("%d
",(DP(r)-DP(l)+X)%X),0;//差分
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF585F.html