【JZOJ3624】【SDOI2014】数数(count) AC自动机+数位dp

题面


100

容易想到使用AC自动机来处理禁忌子串的问题;
然后在自动机上数位dp,具体是:
(f_{i,j,0/1})表示填了(i)位,当前在自动机的第(j)个结点上,(0)表示当前数已经小于了(N)(1)表示当前数等于(N)

Code

#include<bits/stdc++.h>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;i++)
#define fd(i,x,y) for(int i=x;i>=y;i--)
using namespace std;
const char* fin="ex3624.in";
const char* fout="ex3624.out";
const int inf=0x7fffffff;
const int maxn=1507,mo=1000000007;
struct node{
	int x,fa,ne[10];
}ac[maxn];
int n,m,num=1,hd,tl=1;
int f[maxn][maxn][2],ne[maxn][10],b[maxn];
char a[maxn];
void makefail(){
	while (hd++<tl){
		int x=b[hd];
		fo(i,0,9)
			if (ac[x].ne[i]){
				int y=ac[x].ne[i],z=ac[x].fa;
				if (x){
					while (z && !ac[z].ne[i]) z=ac[z].fa;
					ac[y].fa=ac[z].ne[i];
				}
				b[++tl]=y;
				ac[y].x+=ac[ac[y].fa].x;
			}
	}
}
int main(){
	freopen(fin,"r",stdin);
	freopen(fout,"w",stdout);
	scanf("%s",a+1);
	n=strlen(a+1);
	scanf("%d",&m);
	fo(i,1,m){
		char ch=getchar();
		int p=0;
		while (ch<'0' || ch>'9') ch=getchar();
		while (ch>='0' && ch<='9'){
			int x=ch-'0';
			if (!ac[p].ne[x]) ac[p].ne[x]=++num;
			p=ac[p].ne[x];
			ch=getchar();
		}
		ac[p].x++;
	}
	makefail();
	fo(i,0,num){
		if (i==1){
			ne[1][0]=1;
			fo(j,1,9) ne[1][j]=ne[0][j];
			continue;
		}
		fo(j,0,9){
			int x=ac[i].ne[j],y=ac[i].fa;
			if (x){
				if (ac[x].x) ne[i][j]=-1;
				else ne[i][j]=x;
			}else{
				while (y && !ac[y].ne[j]) y=ac[y].fa;
				if (ac[ac[y].ne[j]].x) ne[i][j]=-1;
				else ne[i][j]=ac[y].ne[j];
			}
		}
	}
	memset(f,0,sizeof f);
	f[0][1][0]=1;
	fo(i,0,n-1)
		fo(x,0,num){
			fo(y,0,9){
				if (ne[x][y]==-1) continue;
				int z=ne[x][y],xc=a[i+1]-'0';
				if (y<=xc){
					if (y<xc) f[i+1][z][1]=(f[i+1][z][1]+f[i][x][0])%mo;
					else f[i+1][z][0]=(f[i+1][z][0]+f[i][x][0])%mo;
				}
				f[i+1][z][1]=(f[i+1][z][1]+f[i][x][1])%mo;
			}
		}
	int ans=0;
	fo(x,0,num) ans=((ans+f[n][x][0])%mo+f[n][x][1])%mo;
	ans--;
	printf("%d",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/hiweibolu/p/6914986.html