bzoj3530

3 篇文章 0 订阅
1 篇文章 0 订阅

题目链接:bzoj3530
题解:
这道题很容易可以想到数位dp,如何在保证限制下进行dp呢?
我们可以先将字符串建成ac自动机,这样,我们就可以在dp上加一维表示当前在ac自动机上的哪个点,我们只要dp转移时,不转移给那些不能走的点就可以了,我们用记忆化搜索来解决这个问题。只要在深搜时记录好已经确定的前缀在ac自动机上所走到的节点,我们只从合法状态转移过来就行了。dfs的边界是0,此时返回1。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <algorithm>
using namespace std;
const int L=1210,M=1510;
const int mod=1000000007;
char n[L],s[M];
int son[M][10],end[M],fail[M],line[M];
int f[L][M][2];
bool vis[L][M][2];
int l,cnt,p,root,m;
void insert(){
    int now=root,l=strlen(s+1);
    for (int i=1;i<=l;i++){
        if (!son[now][s[i]-'0'])
            son[now][s[i]-'0']=++p;
        now=son[now][s[i]-'0'];
    }
    end[now]=1;
}
void getfail(){
    int cl=0,op=1;
    line[op]=root;
    for (;cl<op;){
        int now=line[++cl];
        end[now]|=end[fail[now]];
        for (int i=0;i<=9;i++)
            if (!son[now][i])
                son[now][i]=son[fail[now]][i];
            else{
                if (now!=root)
                    fail[son[now][i]]=son[fail[now]][i];
                line[++op]=son[now][i];
            }
    }
}
void add(int &a,int b){
    a=(a+b)%mod;
}
int dfs(int now,int pos,int zero,int up){
    int ans=0;
    if (now==l+1)
        return 1;
    if (zero)
        ans=dfs(now+1,pos,zero,0);
    if (!up&&vis[now][pos][zero]) return f[now][pos][zero];
    int top=9;
    if (up) top=n[now]-'0';
    for (int i=0;i<=top;i++)
        if (!(zero&&i==0)&&end[son[pos][i]]==0)
            add(ans,dfs(now+1,son[pos][i],0,up&&i==top));
    if (!up){
        vis[now][pos][zero]=1;
        f[now][pos][zero]=ans;
    }
    return ans;
}
int main(){
    freopen("3530.in","r",stdin);
    freopen("3530.out","w",stdout);
    scanf("%s",n+1);
    l=strlen(n+1);
    scanf("%d",&m);
    for (int i=1;i<=m;i++){
        scanf("%s",s+1);
        insert();
    }
    getfail();
    int ans=dfs(1,root,1,1);
    ans=(ans-1+mod)%mod;
    printf("%d\n",ans); 
    fclose(stdin);
    fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值