【CF585F】Digits of Number Pi(AC自动机上数位DP)
- 给定一个长为\(n\)的字符串\(s\)和两个长为\(d\)的字符串\(l,r\)。
- 问有多少长为\(d\)的在\([l,r]\)内的字符串,满足存在长度至少为\(\lfloor\frac d2\rfloor\)的子串是\(s\)的子串。
- \(n\le10^3,d\le50\)
\(AC\)自动机上数位\(DP\)
如果我们把\(s\)每个长度为\(\lfloor\frac d2\rfloor\)的子串都取出来建一个\(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\n",(DP(r)-DP(l)+X)%X),0;//差分
}
待到再迷茫时回头望,所有脚印会发出光芒