【bzoj3530】数数
Solution
额其实只是因为。。这种在ac自动机上面拿节点编号当状态的dp方式挺好的但是好像忘得差不多了==导致今天想了一段时间。。所以还是丢上来提醒一下自己吧
实际上一开始想到的是数位dp但是一看这个奇怪的限制以及这个数据范围那。。肯定没有前途
所以我们考虑用一些字符串相关知识来处理
多串匹配什么的。。ac自动机咯,所以我们可以先将\(m\)个数丢到ac自动机里面,然后考虑在上面dp:
记\(f[i][j][0/1/2]\)表示当前这个数已经确定了前\(i\)位,在ac自动机上面的节点\(j\),这个确定的前\(i\)位抠出来组成的数与上限\(n\)的前\(i\)位抠出来组成的数的大小关系(\(0\sim 2\)三个数对应三种状态:等于小于大于),那接下来枚举第一维和第二维以及这一位填什么数,如果儿子为空那就一直跳fail就跟匹配一样,剩下的该怎么转移就怎么正常转就好了
稍微要注意一下的是,因为前导零这种东西十分令人难受,所以我们对于所有的\(f[1][j][0/1/2]\)强行初始化一下。。另外就是转移的时候不能直接赋值而是应该一直累加(都一直跳fail了怎么可能只转移一次),最后就是感觉这种类型的转移的话。。转移有效状态那样会好很多(也就是看\(f[i][j][k]\)能够转到哪里去,并将\(f[i][j][k]\)的值加过去)
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define equal 0
#define smaller 1
#define bigger 2
using namespace std;
const int N=1210,M=110,L=1510,C=10,MOD=1e9+7;
char s[N];
int val[N];
int n,m,ans;
int plu(int x,int y){return (1LL*x+y)%MOD;}
namespace Ac{/*{{{*/
const int N=::L;
queue<int> q;
int ch[N][C],fail[N],nok[N];
int f[::N][N][3];
int tot,rt;
void init(){tot=0; rt=0;}
void insert(char *s){
int now=rt,len=strlen(s),c;
for (int i=0;i<len;++i){
c=s[i]-'0';
if (ch[now][c]==0) ch[now][c]=++tot;
now=ch[now][c];
}
nok[now]=1;
}
void get_fail(){
int u,v;
while (!q.empty()) q.pop();
q.push(rt);
while (!q.empty()){
v=q.front(); q.pop();
for (int i=0;i<C;++i){
if (!ch[v][i]){
ch[v][i]=ch[fail[v]][i];
continue;
}
if (v!=rt){
nok[ch[v][i]]|=nok[ch[fail[v]][i]];
fail[ch[v][i]]=ch[fail[v]][i];
}
else
fail[ch[v][i]]=rt;
q.push(ch[v][i]);
}
}
}
int cmp(int x,int y){
if (x==y) return equal;
if (x<y) return smaller;
if (x>y) return bigger;
}
void dp(){
memset(f,0,sizeof(f));
for (int i=1;i<=9;++i)
if (!nok[ch[rt][i]])
f[1][ch[rt][i]][cmp(i,val[1])]+=1;
int x,st;
for (int i=1;i<n;++i){
for (int j=0;j<=tot;++j){
if (!f[i][j][0]&&!f[i][j][1]&&!f[i][j][2]) continue;
for (int k=0;k<C;++k){
x=ch[j][k];
if (nok[x]) continue;
st=cmp(k,val[i+1]);
if (st==equal){
f[i+1][x][equal]=plu(f[i+1][x][equal],f[i][j][equal]);
f[i+1][x][smaller]=plu(f[i+1][x][smaller],f[i][j][smaller]);
f[i+1][x][bigger]=plu(f[i+1][x][bigger],f[i][j][bigger]);
}
else if (st==smaller){
f[i+1][x][smaller]=plu(f[i+1][x][smaller],plu(f[i][j][smaller],f[i][j][equal]));
f[i+1][x][bigger]=plu(f[i+1][x][bigger],f[i][j][bigger]);
}
else{//bigger
f[i+1][x][smaller]=plu(f[i+1][x][smaller],f[i][j][smaller]);
f[i+1][x][bigger]=plu(f[i+1][x][bigger],plu(f[i][j][equal],f[i][j][bigger]));
}
}
}
}
ans=0;
for (int i=1;i<=n;++i){
for (int j=0;j<=tot;++j){
ans=plu(ans,f[i][j][smaller]+f[i][j][equal]);
if (i<n)
ans=plu(ans,f[i][j][bigger]);
}
}
}
}/*}}}*/
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%s",s);
n=strlen(s);
for (int i=0;i<n;++i) val[i+1]=s[i]-'0';
scanf("%d",&m);
Ac::init();
for (int i=1;i<=m;++i)
scanf("%s",s),Ac::insert(s);
Ac::get_fail();
Ac::dp();
printf("%d\n",ans);
}