您的位置:首页 > 其它

【bzoj3530】【sdoi2014】【数数】【AC自动机+数位dp】

2016-07-08 08:27 483 查看

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

题解:

            S中的串可能会有前导0,所以我们把串反过来,然后反着dp.

            首先对S中的所有反串建AC自动机.

            f[i][j][0/1]表示到倒数第i位,匹配到AC自动机的第j个节点,是否比原数小.

            枚举i,j,0/1转移即可.在dp的过程中可以顺便统计答案.
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 2010
#define P 1000000007
int n,a
[10],pos
,m,q
,fail
,ans,f

[2],cnt=1;
char ch
,s
;
using namespace std;
int read(){
int x(0);char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
void insert(){
int len=strlen(ch),now=1;
for (int i=len-1;i>=0;i--){
int u=ch[i]-'0';
if (a[now][u]) now=a[now][u];
else a[now][u]=++cnt,now=a[now][u];
}
pos[now]=1;
}
void getfail(){
int h(0),t(1);
q[0]=1;fail[1]=0;
while (h<t){
int u=q[h++];
for (int i=0;i<=9;i++){
if (!a[u][i]) continue;
int k=fail[u];
while (!a[k][i]) k=fail[k];
fail[a[u][i]]=a[k][i];
if (pos[a[k][i]]) pos[a[u][i]]=1;
q[t++]=a[u][i];
}
}
}
int main(){
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
scanf("%s",s);
m=read();n=strlen(s);
for (int i=0;i<=9;i++) a[0][i]=1;
for (int i=1;i<=m;i++){
scanf("%s",ch);
insert();
}
getfail();
//for (int i=1;i<=cnt;i++) cout<<fail[i]<<endl;
f
[1][0]=1;
for (int i=n;i>=1;i--)
for (int j=1;j<=cnt;j++)
for (int p=0;p<=1;p++)
if (f[i][j][p])
for (int k=0;k<=9;k++){
int u=s[i-1]-'0',t=j,o;
while (!a[t][k]) t=fail[t];
t=a[t][k];if (pos[t]) continue;
o=k>u||(k==u&&p);(f[i-1][t][o]+=f[i][j][p])%=P;
if (k&&!(i==1&&o)) (ans+=f[i][j][p])%=P;
}
cout<<ans<<endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: