题意:
求1~n中不含有集合S中的元素为子串,不含前导0。
题解:
一开始傻傻的写了之前的数位dp版本。
换了种打法,
f[i][j][k]
表示前i位,在自动机j号点上。
k=0为小于n,k=1等于,k=2大于。
然后瞎转移。
code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
#include<algorithm>
#define LL long long
using namespace std;
const int mod=1000000007;
struct trnode{
int a[11],fail;
bool tail;
trnode(){tail=false;}
}tr[2000];int root=1,tot=1;
int f[1210][2000][4];
int m,n,a[1210];
char s[1510];
void build()
{
int len=strlen(s+1);
int x=root;
for(int i=1;i<=len;i++)
{
int c=s[i]-'0';
if(tr[x].a[c]) x=tr[x].a[c];
else x=tr[x].a[c]=++tot;
}
tr[x].tail=true;
}
queue<int> q;
void make_root()
{
q.push(root);
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=0;i<=9;i++)
{
if(!tr[x].a[i]) continue;
if(x==root) tr[tr[x].a[i]].fail=root;
else
{
int j=tr[x].fail;
while(j!=root&&!tr[j].a[i]) j=tr[j].fail;
tr[tr[x].a[i]].fail=tr[j].a[i];
tr[tr[x].a[i]].tail|=tr[tr[j].a[i]].tail;
}
q.push(tr[x].a[i]);
}
}
}
int qs(int k,int x)
{
if(k==x) return 1;
if(k<x) return 0;
return 2;
}
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=0;i<=9;i++) tr[root].a[i]=++tot;
for(int i=1;i<=m;i++)
{
scanf("%s",s+1);
build();
}
make_root();
f[0][root][1]=1;
for(int i=0;i<n;i++)
for(int x=1;x<=tot;x++)
for(int K=0;K<3;K++)
{
if(tr[x].tail||f[i][x][K]==0) continue;
for(int c=0;c<=9;c++)
{
if(x==root&&c==0) continue;
int k=x;
while(!tr[k].a[c]) k=tr[k].fail;
if(!tr[tr[k].a[c]].tail)
{
if(K!=1) (f[i+1][tr[k].a[c]][K]+=f[i][x][K])%=mod;
else (f[i+1][tr[k].a[c]][qs(c,a[i+1])]+=f[i][x][K])%=mod;
}
}
}
int ans=0;
for(int i=1;i<=n;i++)
for(int j=2;j<=tot;j++)
for(int k=0;k<=2;k++)
if(k<=1||i!=n) (ans+=f[i][j][k])%=mod;
printf("%d",ans);
}