
在ac自动机上dp,dpij0表示到第i位ac自动机上第j个节点前面严格小于n的方案数,转移时能到0至9,1是与n相等的方案数,转移到比后一位小的0,和等于后一位的1,如果当前碰到在集合中的串了,就不转移。为了防止前导0,每一位从根向外++,最后统计答案就是dpl-1i0和dpl-1i1的总和(不包括集合中的串)。
#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;
#define ll long long int
const int mod=1e9+7;
struct node
{
ll nxt[10];
bool flag;
ll fail;
}tree[1501];
char n[1201],s[1501];
ll dp[1201][1501][2];
ll l,m,cnt=0;
void insert(ll len)
{
ll i,x=0;
for(i=0;i<len;i++)
{
if(!tree[x].nxt[s[i]-'0'])
tree[x].nxt[s[i]-'0']=++cnt;
x=tree[x].nxt[s[i]-'0'];
}
tree[x].flag=1;
}
void getfail()
{
queue<ll>q;
ll i;
for(i=0;i<=9;i++)
{
if(tree[0].nxt[i])
q.push(tree[0].nxt[i]);
}
while(!q.empty())
{
ll x=q.front();
q.pop();
for(i=0;i<=9;i++)
{
if(tree[x].nxt[i])
{
tree[tree[x].nxt[i]].fail=tree[tree[x].fail].nxt[i];
if(tree[tree[tree[x].fail].nxt[i]].flag==1)//如果fail指针指向的节点有flag,它也必须有
tree[tree[x].nxt[i]].flag=1;
q.push(tree[x].nxt[i]);
}
else
tree[x].nxt[i]=tree[tree[x].fail].nxt[i];//ac自动机的精髓,必须加
}
}
}
int main()
{
scanf("%s",n);
l=strlen(n);
scanf("%lld",&m);
ll i;
for(i=1;i<=m;i++)
{
scanf("%s",s);
ll len=strlen(s);
insert(len);
}
getfail();
for(i=1;i<n[0]-'0';i++)
dp[0][tree[0].nxt[i]][0]++;
dp[0][tree[0].nxt[n[0]-'0']][1]++;
for(i=0;i<=l-2;i++)
{
ll j;
for(j=1;j<=9;j++)
dp[i+1][tree[0].nxt[j]][0]++;//前面都是0的情况,保证不重
for(j=0;j<=cnt;j++)
{
if(tree[j].flag)
continue;
ll k;
for(k=0;k<=9;k++)
dp[i+1][tree[j].nxt[k]][0]=(dp[i+1][tree[j].nxt[k]][0]+dp[i][j][0])%mod;
for(k=0;k<n[i+1]-'0';k++)
dp[i+1][tree[j].nxt[k]][0]=(dp[i+1][tree[j].nxt[k]][0]+dp[i][j][1])%mod;
dp[i+1][tree[j].nxt[n[i+1]-'0']][1]=(dp[i+1][tree[j].nxt[n[i+1]-'0']][1]+dp[i][j][1])%mod;
}
}
ll ans=0;
for(i=0;i<=cnt;i++)
{
if(!tree[i].flag)
ans=(ans+dp[l-1][i][0]+dp[l-1][i][1])%mod;
}
printf("%lld\n",ans%mod);
return 0;
}
浙公网安备 33010602011771号