您的位置:首页 > 其它

【HDU3530】 [Sdoi2014]数数 (AC自动机+数位DP)

2016-07-14 10:48 477 查看

3530: [Sdoi2014]数数

Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 682 Solved: 364

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。
  话说数位DP搞了我好久。主要是联系上AC自动机判病毒串的时候有点卡- -(脑子一片混乱
  dp方程:f[i][j]表示现在在点j,继续走i步(不经病毒点)的方案数。
  先把长度小于n的加入ans,我是for了一遍长度累加的(前缀0那里有点坑,so...)
  然后手动填与n长度相等的串,for一下,判断一下,累加一下,就好了。。 你懂的...

  

  主要部分:

  

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define Maxn 1600
#define Maxl 1600
#define Mod 1000000007

struct node
{
int fail,mark;
int son[30];
}t[Maxn];int tot;

int m,sl;

void upd(int x)
{
t[x].mark=0;
memset(t[x].son,0,sizeof(t[x].son));
}

char s[Maxl];
char ss[Maxn];
void read_trie()
{
scanf("%s",s+1);
int len=strlen(s+1);
int now=0;
for(int i=1;i<=len;i++)
{
int ind=s[i]-'0'+1;
if(!t[now].son[ind])
{
t[now].son[ind]=++tot;
upd(tot);
}
now=t[now].son[ind];
if(i==len) t[now].mark=1;
}
}

queue<int > q;
void build_AC()
{
while(!q.empty()) q.pop();
q.push(0);
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=1;i<=10;i++)
{
if(t[x].son[i])
{
t[t[x].son[i]].fail=x?t[t[x].fail].son[i]:0;
q.push(t[x].son[i]);
}
else t[x].son[i]=t[t[x].fail].son[i];
}
if(t[t[x].fail].mark) t[x].mark=1;
}
}

void init()
{
scanf("%s",ss+1);
sl=strlen(ss+1);
scanf("%d",&m);
tot=0;upd(0);
for(int i=1;i<=m;i++) read_trie();
build_AC();
}

int check()
{
for(int i=1;i<=sl;i++)
{
bool p=1;
int now=0;
for(int j=i;j>=1;j--)
{
if(t[ t[now].son[ss[j]-'0'+1] ].mark) {p=0;break;}
now=t[now].son[ss[j]-'0'+1];
}
if(!p) return i;
}
return 0;
}

int f[Maxn][Maxn];
void dp()
{
memset(f,0,sizeof(f));
for(int i=0;i<=tot;i++) f[0][i]=1;//走到i点,继续填0个数的方案

for(int i=1;i<=sl;i++)
{
for(int j=0;j<=tot;j++) if(!t[j].mark)
{
for(int k=1;k<=10;k++) if(!t[t[j].son[k]].mark)
f[i][j]=(f[i][j]+f[i-1][t[j].son[k]])%Mod;
}
}

int ans=0;
if(sl!=1)
{
for(int j=2;j<=sl;j++)
for(int i=2;i<=10;i++) if(!t[t[0].son[i]].mark)
ans=(ans+f[sl-j][t[0].son[i]])%Mod;
}

int now=0;
bool ok=1;
for(int i=sl;i>=1;i--)
{
for(int k=0;k<ss[sl-i+1]-'0';k++)//枚举第i位填的数
{
if(i==sl&&k==0) continue;
if(t[t[now].son[k+1]].mark) continue;
ans=(ans+f[i-1][t[now].son[k+1]])%Mod;
}
now=t[now].son[ss[sl-i+1]-'0'+1];
if(t[now].mark) {ok=0;break;}
}
if(ok) ans=(ans+1)%Mod;
if(sl==1&&ss[1]=='0') ans=0;
printf("%d\n",ans);
}

int main()
{
init();
dp();
return 0;
}


[HDU3530]

2016-07-14 10:51:01
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: