题目描述
我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串。例如当S=(22,333,0233)时,233是幸运数,2333、20233、3223不是幸运数。 给定N和S,计算不大于N的幸运数个数。
输入输出格式
输入格式:
输入的第一行包含整数N。 接下来一行一个整数M,表示S中元素的数量。 接下来M行,每行一个数字串,表示S中的一个元素。
输出格式:
输出一行一个整数,表示答案模
1
0
9
+
7
10^9+7
109+7的值。
输入输出样例
输入样例#1:
20
3
2
3
14
输出样例#1:
14
说明
下表中l表示N的长度,L表示S中所有串长度之和。
1 < =l < =1200 , 1 < =M < =100 ,1 < =L < =1500
分析:
对于S中的元素,可以把AC自动机建出来。并且每个节点维护一个
t
a
g
tag
tag表示fail链上是否存在一个节点是某个字符串的末尾。
然后就可以在AC自动机上面dp了。
设
f
[
i
]
[
j
]
[
0
/
1
]
f[i][j][0/1]
f[i][j][0/1]表示前
i
i
i位,现在在AC自动机的第
j
j
j个节点(也许这个节点不代表
[
1
,
i
]
[1,i]
[1,i]的字符串,但一定是最长后缀),是否达到上限的答案。
转移枚举最后一位,并判断新加入字符后的串的
t
a
g
tag
tag是否为0。
最后原串不含前导0,我们直接在建好自动机后把
c
h
[
0
]
.
s
o
n
[
0
]
=
0
ch[0].son[0]=0
ch[0].son[0]=0即可,这样不会影响通过条
f
a
i
l
fail
fail到达
c
h
[
0
]
.
s
o
n
[
0
]
ch[0].son[0]
ch[0].son[0],只会影响直接到达。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <queue>
const int maxn=1507;
const int mod=1e9+7;
using namespace std;
int n,m,cnt,ans;
int f[maxn][maxn][2],a[maxn];
char s[maxn];
struct node{
int fail;
int son[10];
bool flag;
}t[maxn];
void build()
{
int len=strlen(s+1),now=0;
for (int i=1;i<=len;i++)
{
int c=s[i]-'0';
if (!t[now].son[c]) t[now].son[c]=++cnt;
now=t[now].son[c];
}
t[now].flag=1;
}
void getfail()
{
queue <int> q;
for (int i=0;i<10;i++)
{
if (t[0].son[i]) q.push(t[0].son[i]);
}
while (!q.empty())
{
int now=q.front();
q.pop();
t[now].flag|=t[t[now].fail].flag;
for (int i=0;i<10;i++)
{
if (t[now].son[i])
{
t[t[now].son[i]].fail=t[t[now].fail].son[i];
q.push(t[now].son[i]);
}
else t[now].son[i]=t[t[now].fail].son[i];
}
}
t[0].son[0]=0;
}
void add(int &x,int y)
{
x+=y;
if (x>=mod) x-=mod;
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
for (int i=1;i<=n;i++) a[i]=s[i]-'0';
scanf("%d",&m);
for (int i=1;i<=m;i++)
{
scanf("%s",s+1);
build();
}
getfail();
f[0][0][1]=1;
for (int i=1;i<=n;i++)
{
for (int j=0;j<=cnt;j++)
{
for (int k=0;k<=9;k++)
{
int next=t[j].son[k];
if (t[next].flag) continue;
add(f[i][next][0],f[i-1][j][0]);
if (k<a[i]) add(f[i][next][0],f[i-1][j][1]);
if (k==a[i]) add(f[i][next][1],f[i-1][j][1]);
}
}
}
ans=mod-1;
for (int i=0;i<=cnt;i++) add(ans,f[n][i][0]),add(ans,f[n][i][1]);
printf("%d\n",ans);
}