BZOJ3530 SDOI2014 数数

传送门

题目大意

给定一个长度为$l$的数$n(lleq 1200)$,给定$m$个可能包含前导$0$的数字串$mleq 100$,求有多少个不超过$n$的的数在去前导零后不包含任何一个子串数字串。

题解

很裸的$AC$自动机$+$数位$Dp$题。

对于$m$个数字串建$AC$自动机,$ F_{i,j,k={0,1}}$表示前$i$位匹配到$AC$自动机上的第$j$个节点,有$(k=1)$无$(k=0)$卡到数的上界的方案数,遇到结尾数字串的时候不进行转移即可。关于$n$前导$0$不能对匹配产生影响的问题,我们不能使用传统的令第最高$+1$位卡上界的初始状态答案为$1$,而是要枚举$n$的最高位在哪一位,枚举最高为是几,把这些位在$AC$自动机上的初始状态的$DP$值设为$1$,然后照常做即可。

然后就是写$AC$自动机的时候一定要把$x$的标记从$fail_x$处转移过来,不然会出事情,比如$BZOJ$讨论群中的

1234
2
121
2

正确代码输出:890
错误代码输出:930

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 1510
#define mod 1000000007
using namespace std;
int read(){
    int nm=0,fh=1; char cw=getchar();
    for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    return nm*fh;
}
int n,m,fd[M],t[M][10],q[M<<1],hd,tl,rt,cnt,F[M][M][2],ans;
void upd(int &x,int y){x=((x+y>=mod)?x+y-mod:x+y);}
char ch[M],s[M]; bool vis[M];
void ins(int &x,char *k,int rem){
    if(!x) x=++cnt; if(!rem){vis[x]=true;return;}
    ins(t[x][*k-'0'],k+1,rem-1);
}
int main(){
    scanf("%s",ch+1),n=strlen(ch+1);
    for(int T=read();T;--T) scanf("%s",s),ins(rt,s,strlen(s));
    for(int i=0;i<10;i++) if(t[rt][i]) fd[t[rt][i]]=1,q[tl++]=t[rt][i]; else t[rt][i]=1;
    for(fd[rt]=rt;hd<tl;){
        int x=q[hd++]; vis[x]|=vis[fd[x]];
        for(int k=0;k<10;k++){
            if(!t[x][k]) t[x][k]=t[fd[x]][k];
            else fd[t[x][k]]=t[fd[x]][k],q[tl++]=t[x][k];
        }
    }
    for(int i=1;i<=ch[1]-'0';i++) if(!vis[t[rt][i]]) F[1][t[rt][i]][i==(ch[1]-'0')]++;
    for(int i=2;i<=n;i++) for(int j=1;j<10;j++) if(!vis[t[rt][j]]) F[i][t[rt][j]][0]++;
    for(int i=1;i<=n;i++){
        for(int k=1;k<=cnt;k++){
            if(vis[k]){F[i][k][0]=F[i][k][1]=0;continue;}
            if(i==n){upd(ans,F[i][k][0]),upd(ans,F[i][k][1]);continue;}
            if(F[i][k][0]) for(int D=0;D<10;D++) upd(F[i+1][t[k][D]][0],F[i][k][0]);
            if(F[i][k][1]) for(int D=0;D<=ch[i+1]-'0';D++) upd(F[i+1][t[k][D]][D+'0'==ch[i+1]],F[i][k][1]);
        }
    } printf("%d
",ans); return 0;
}
原文地址:https://www.cnblogs.com/OYJason/p/9837724.html