BZOJ.3530.[SDOI2014]數數(AC自動機 數位DP)
阿新 • • 發佈:2018-03-11
數位dp 上界 const 模式串 turn while 不可 ring href
題目鏈接
首先數位DP 用f[i][0/1]表示匹配到第i位前面i-1位是否為上界。
這樣還需要狀態轉移,對於每個狀態 枚舉每一個數,用AC自動機得到下一個狀態(這樣狀態其實就是在樹上的標號)
ps1.數不能帶前導0(因為會有00...這樣的串),對每一位要先加上以每個非零數開頭的方案數
ps2.如果某個點能沿著fail跳回到一個模式串節點,它也是不可行的
//15072kb 416ms
#include <cstdio>
#include <cstring>
#define mod 1000000007
const int N=1505,S=10;
int n,m,A[N],f[1205][2][1505 ];
char s[N],tmp[N];
struct AC_Automaton
{
int cnt,son[N][S],fail[N],q[N];
bool val[N];
void Insert(char *s)
{
int x=0;
for(int t,i=0,l=strlen(s); i<l; ++i)
{
if(!son[x][t=s[i]-'0']) son[x][t]=++cnt;
x=son[x][t];
}
val[x]=1 ;
}
void Build()
{
int h=0,t=0;
for(int i=0; i<S; ++i)
if(son[0][i]) fail[son[0][i]]=0,q[t++]=son[0][i];
while(h<t)
{
int x=q[h++];
val[x]|=val[fail[x]];//!
for(int i=0; i<S; ++i)
if(son[x][i]) fail[son[x][i]]=son[fail[x]][i],q[t++]=son[x][i];
else son[x][i]=son[fail[x]][i];
}
}
}ac;
inline void Add(int &x,int v) {x+=v, x>=mod?x-=mod:0;}
int main()
{
scanf("%s%d",s+1,&m), n=strlen(s+1);
for(int i=1; i<=n; ++i) A[i]=s[i]-'0';
for(int i=1; i<=m; ++i) scanf("%s",tmp),ac.Insert(tmp);
ac.Build();
int now=1,las=0;
for(int i=1; i<A[1]; ++i) ++f[1][0][ac.son[0][i]];//不計算0
/*if(!ac.val[ac.son[0][A[1]]])*/ f[1][1][ac.son[0][A[1]]]=1;
for(int i=2; i<=n; ++i)
{
for(int j=1; j<S; ++j)
++f[i][0][ac.son[0][j]];//以當前每個數開頭
for(int j=0; j<=ac.cnt; ++j)
if(!ac.val[j])
{
for(int k=0; k<S; ++k)
Add(f[i][0][ac.son[j][k]],f[i-1][0][j]);
for(int k=0; k<A[i]; ++k)
Add(f[i][0][ac.son[j][k]],f[i-1][1][j]);
Add(f[i][1][ac.son[j][A[i]]],f[i-1][1][j]);
}
}
int res=0;
for(int i=0; i<=ac.cnt; ++i)
if(!ac.val[i]) Add(res,(f[n][0][i]+f[n][1][i])%mod/*這帶個取模*/);
printf("%d",res);
return 0;
}
BZOJ.3530.[SDOI2014]數數(AC自動機 數位DP)