P3311 [SDOI2014]数数

传送门

显然直接 $AC$ 自动机上数位 $dp$ 一下

预处理出 $f[i][j]$ 表示当前匹配到 $AC$ 自动机上的节点 $j$ ,再放 $i$ 个位的数字后不冲突的方案数

初始时 $f[0][j]=1$ ,其中 $j$ 不是匹配节点(匹配节点显然指的是本身是某个模式串的结束节点或者 $fail$ 树上祖先存在某个节点是结束节点)

然后把 $n$ 放到自动机上走,其实就是普通的数位 $dp$ ,每到一个节点都枚举所有小于这一位的数字,然后把 $f$ 相应的方案数加入答案

然后一旦 $n$ 走到自动机的匹配节点了就直接 $return$

如果 $n$ 走完了也没有到匹配节点,那么等于 $n$ 的这个数也是一种合法方案,要记得答案 $+1$,那么数位 $dp$ 的代码很简单:

void dfs(int x,int p)//当前在节点x,考虑到n的第p位
{
    if(pd[x]) return;//不合法了直接return
    if(p>tot) { ans++; return; }//n是合法的答案要包括n
    for(int i=0;i<n[p]-'0';i++) ans=(ans+f[tot-p][c[x][i]])%mod;//枚举这一位小于n[p]的数,剩下的位就可以随便填
    dfs(c[x][n[p]-'0'],p+1);
}

 

然后发现只有 $80$ 分,这是因为模式串可能有前导 $0$ ,我们预处理 $f$ 的时候也是有考虑前导 $0$ 的情况,但是真正填数的时候是没有前导零的

所以在 $n$ 的第一位之前,我们枚举小于 $n[1]$ 的数时不能枚举 $0$ ,因为这样就变成了有前导零的情况

我们必须在最后再特殊考虑位数不足 $n$ 的情况:

for(int i=0;i<tot-1;i++)//枚举总位数不足n的位数的情况
    for(int j=1;j<=9;j++) ans=(ans+f[i][c[0][j]])%mod;//枚举第一个数

 

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=2007,mo=1e9+7;
inline int fk(int x) { return x>=mo ? x-mo : x; }
int tot,m;
char s[N],n[N];
int c[N][11],fail[N],cnt;
bool pd[N];
void ins()
{
    int u=0,len=strlen(s+1);
    for(int i=1;i<=len;i++)
    {
        int v=s[i]-'0';
        if(!c[u][v]) c[u][v]=++cnt;
        u=c[u][v];
    }
    pd[u]=1;
}
void build()
{
    queue <int> Q;
    for(int i=0;i<=9;i++) if(c[0][i]) Q.push(c[0][i]);
    while(!Q.empty())
    {
        int x=Q.front(); Q.pop();
        for(int i=0;i<=9;i++)
        {
            int &v=c[x][i];
            if(!v) v=c[fail[x]][i];
            else fail[v]=c[fail[x]][i],pd[v]|=pd[fail[v]],Q.push(v);
        }
    }
}
int f[N][N],ans;
void pre()
{
    for(int i=0;i<=cnt;i++) if(!pd[i]) f[0][i]=1;
    for(int i=1;i<=tot;i++)
        for(int j=0;j<=cnt;j++)
        {
            if(pd[j]) continue;
            for(int k=0;k<=9;k++)
                f[i][j]=fk(f[i][j]+f[i-1][c[j][k]]);
        }
}
void dfs(int x,int p)
{
    if(pd[x]) return;
    if(p>tot) { ans++; return; }
    for(int i=(p==1);i<n[p]-'0';i++) ans=fk(ans+f[tot-p][c[x][i]]);
    dfs(c[x][n[p]-'0'],p+1);
}
int main()
{
    scanf("%s",n+1); m=read(); tot=strlen(n+1);
    for(int i=1;i<=m;i++) scanf("%s",s+1),ins();
    build(); pre(); dfs(0,1);
    for(int i=0;i<tot-1;i++)
        for(int j=1;j<=9;j++) ans=fk(ans+f[i][c[0][j]]);
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2019-09-22 15:48  LLTYYC  阅读(229)  评论(0编辑  收藏  举报