zoukankan      html  css  js  c++  java
  • BZOJ3530:[SDOI2014]数数(AC自动机,数位DP)

    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

    Solution

    一个挺简单的一个题……建出来$AC$自动机然后在上面直接跑数位$DP$就好了。只不过有点小地方需要注意。

    用$DFS(zero,lim,pos,now)$是否有前导0,是否卡上界,第$pos$位,自动机上第$now$个点。

    为什么要存前导0呢?我们可以发现有这么一个例子:

    10

    1

    01

    这个跑出来应该是10,然而不记前导零特判一下会跑出来9。这是因为当幸运串为01的时候我们会忽略前导0,所以是合法的。

    只需要在$DFS$的时候特判一下,如果有前导0,且幸运数这一位选0,且$now$还在根节点,就让$now$停在根节点就好了。

    建立AC自动机的时候,如果某个节点能够沿着fail指针跳到单词节点,那么这个节点也应当禁止通过……

    自测一时爽

    Code

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<queue>
     5 #define N (1509)
     6 #define MOD (1000000007)
     7 using namespace std;
     8 
     9 int sz,Son[N][11],Fail[N],End[N];
    10 int cnt,m,x,f[N][N],a[N],spc[N];
    11 char n[N],s[N];
    12 queue<int>q;
    13 
    14 void Insert(char s[])
    15 {
    16     int now=0;
    17     for (int i=0,l=strlen(s); i<l; ++i)
    18     {
    19         int c=s[i]-'0';
    20         if (!Son[now][c]) Son[now][c]=++sz;
    21         now=Son[now][c];
    22     }
    23     End[now]++;
    24 }
    25 
    26 void Build_Fail()
    27 {
    28     for (int i=0; i<=9; ++i)
    29         if (Son[0][i]) q.push(Son[0][i]);
    30     while (!q.empty())
    31     {
    32         int now=q.front(); q.pop();
    33         for (int i=0; i<=9; ++i)
    34         {
    35             if (!Son[now][i])
    36             {
    37                 Son[now][i]=Son[Fail[now]][i];
    38                 continue;
    39             }
    40             Fail[Son[now][i]]=Son[Fail[now]][i];
    41             q.push(Son[now][i]);
    42         }
    43     }
    44 }
    45 
    46 int DFS(int zero,int lim,int pos,int now)
    47 {
    48     if (pos==0) return 1;
    49     if (!zero && !lim && f[pos][now]!=-1) return f[pos][now];
    50     f[pos][now]=0;
    51     int up=lim?n[pos]-'0':9;
    52     for (int i=0; i<=up; ++i)
    53         if (!End[Son[now][i]])
    54         {
    55             if (zero && !i && !now) (f[pos][now]+=DFS(zero,lim&&0==up,pos-1,0))%=MOD;
    56             else
    57             {
    58                 int flag=1,t=Son[now][i];
    59                 while (t)
    60                 {
    61                     if (End[t]) {flag=0; break;}
    62                     t=Fail[t];
    63                 }
    64                 if (!flag) continue;
    65                 (f[pos][now]+=DFS(zero&&!i,lim&&i==up,pos-1,Son[now][i]))%=MOD;
    66             }
    67         }
    68     return f[pos][now];
    69 }
    70 
    71 int main()
    72 {
    73     memset(f,-1,sizeof(f));
    74     scanf("%s%d",n+1,&m); cnt=strlen(n+1);
    75     for (int i=1; i<=m; ++i)
    76         scanf("%s",s),Insert(s);
    77     Build_Fail(); 
    78     for (int i=1,j=cnt; i<j; ++i,--j)
    79         swap(n[i],n[j]);
    80     printf("%d
    ",DFS(1,1,cnt,0)-1);
    81 }
  • 相关阅读:
    Max History CodeForces
    Buy a Ticket CodeForces
    AC日记——字符串的展开 openjudge 1.7 35
    AC日记——回文子串 openjudge 1.7 34
    AC日记——判断字符串是否为回文 openjudge 1.7 33
    AC日记——行程长度编码 openjudge 1.7 32
    AC日记——字符串P型编码 openjudge 1.7 31
    AC日记——字符环 openjudge 1.7 30
    AC日记——ISBN号码 openjudge 1.7 29
    AC日记——单词倒排 1.7 28
  • 原文地址:https://www.cnblogs.com/refun/p/10037975.html
Copyright © 2011-2022 走看看