3530: [Sdoi2014]数数

Description

我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串。例如当S=(22,333,0233)时,233是幸运数,2333、20233、3223不是幸运数。
给定N和S,计算不大于N的幸运数个数。

Input

输入的第一行包含整数N。
接下来一行一个整数M,表示S中元素的数量。
接下来M行,每行一个数字串,表示S中的一个元素。

Output

输出一行一个整数,表示答案模109+7的值。

Sample Input

20

3

2

3

14

Sample Output

14

HINT
下表中l表示N的长度,L表示S中所有串长度之和。
1 < =l < =1200 , 1 < =M < =100 ,1 < =L < =1500

题解

就先建一个AC自动机
然后做一下数位DP就好了
代码很丑,看看数组的表示就好了

要注意的是,2和02不是一个东西

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int MOD=1e9+7;
const int N=1505;
struct node{
     int son[10],fail;
     bool tf;//是不是终点 
}s[N];int num=0;
char ss[N];
int m;
void init (int x)
{
    //printf("%d\n",x);
    s[x].tf=false;s[x].fail=0;
    for (int u=0;u<10;u++) s[x].son[u]=-1;
}
void bt ()
{
    int len=strlen(ss),now=0;
    for (int u=0;u<len;u++)
    {
        int x=ss[u]-'0';
        if (s[now].son[x]==-1)
        {
            s[now].son[x]=++num;
            init(num);
        }
        now=s[now].son[x];
    }
    //printf("YES:%d\n",now);
    s[now].tf=true;
}
queue<int> q;
void bfs ()
{
    int x;q.push(0);
    while (!q.empty())
    {
        int x=q.front();
        for (int u=0;u<10;u++)
        {
            int son=s[x].son[u],f=s[x].fail;
            if (son>=0)
            {
                if (x==0) s[son].fail=0;
                else s[son].fail=s[f].son[u];
                q.push(son);
            }
            else
            {
                if (x==0) s[x].son[u]=0;
                else s[x].son[u]=s[f].son[u];
            }
        }
        q.pop();
    }
}
bool vis[N];
void dfs (int x)
{
    if (vis[x]) return ;
    vis[x]=true;
    int Fail=s[x].fail;
    dfs(Fail);
    s[x].tf=(s[x].tf|s[Fail].tf);
}
char n[N];
int ooo;
int f[N][2][2][N];
int solve (int x,bool tf,bool tf1,int y)//当前到第几位了    是否已经小了 是否非0位    在AC自动机上是哪一位
{
    if (tf1==false) y=0;
    if (s[y].tf==true) return 0;
//  printf("%d %d %d %d\n",x,tf,tf1,y);
    if (x>ooo) return tf1==false?0:1;
    if (f[x][tf][tf1][y]!=0) return f[x][tf][tf1][y];
    if (tf==true)//可以随便填 
    {
        f[x][tf][tf1][y]=(f[x][tf][tf1][y]+solve(x+1,true,tf1,s[y].son[0]))%MOD;//填0 
        for (int u=1;u<=9;u++)
        {
        //  printf("NO:%d %d %d %d\n",x,u,solve(x+1,true,true,s[y].son[u]),s[y].son[u]);
            f[x][tf][tf1][y]=(f[x][tf][tf1][y]+solve(x+1,true,true,s[y].son[u]))%MOD;
        }
    }
    else
    {
        int now=n[x]-'0';
        if (0!=now) f[x][tf][tf1][y]=(f[x][tf][tf1][y]+solve(x+1,true,tf1,s[y].son[0]))%MOD;
        for (int u=1;u<now;u++) 
            f[x][tf][tf1][y]=(f[x][tf][tf1][y]+solve(x+1,true,true,s[y].son[u]))%MOD;
        if (now==0) f[x][tf][tf1][y]=(f[x][tf][tf1][y]+solve(x+1,false,tf1,s[y].son[now]))%MOD;
        else f[x][tf][tf1][y]=(f[x][tf][tf1][y]+solve(x+1,false,true,s[y].son[now]))%MOD;
    }
//  printf("YES:%d %d %d %d %d\n",x,tf,tf1,y,f[x][tf][tf1][y]);
    return f[x][tf][tf1][y]; 
}
int main()
{
    memset(f,0,sizeof(f));
    scanf("%s",n+1); ooo=strlen(n+1);
    scanf("%d",&m);
    init(0);
    for (int u=1;u<=m;u++)
    {
        scanf("%s",ss);
        bt();
    }
    bfs();
    memset(vis,false,sizeof(vis));vis[0]=true;
    for (int u=1;u<=num;u++)    
    {
        //if (s[u].tf) printf("OZY:%d\n",u);
        dfs(u);
    }
    printf("%d\n",(solve(1,false,false,0)+MOD)%MOD);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值