SDOI2014 数数
题目链接:戳我
AC自动机+动态规划。
我们设\(dp[1/2/3][i][j]\)来表示不同的状态,i表示处理到文本串第i位,j表示处理到AC自动机上节点j。
因为这道题有前导零,所以我们可以参考一下数位DP的思想,我们区分有限制和没有限制的情况分类套路进行AC自动机上DP。
3表示没有任何限制的DP。当当前处理的字符串长度小于文本串长度的时候,显然我们可以累积合法答案。
\(dp[3][ac[now].t[k]+=dp[3][i][now]\)
2表示有限制的DP。当前处理的字符串等于当前文本串的长度。当前处理位已经达到限制,显然上一位也应当达到限制。我们应当从上一位达到最大限制的状态转移而来。
1也表示有限制的DP。当前处理的字符串等于当前文本串的长度。当前处理位没有达到限制,所以有两种转移方法,一种是从上一个合法的自己转移而来,一种是从2转移而来。
我们给字符串结尾打上标记,之后匹配的时候如果匹配到一定要跳过,因为匹配上就是不合法情况。还有一个,因为是在AC自动机上搞DP,前后有继承性,所以每个节点一定要继承一下它的fail指针指向的点的标记。qwq
但是要注意,023等价于23,所以一定要注意i0&&k0的时候不合法!!
具体转移可以参考代码。
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#define MAXN 2000
#define mod 1000000007
using namespace std;
int n,cnt,lenth;
int dp[4][MAXN][MAXN];
long long ans;
char s[MAXN],N[MAXN];
struct Node{int fail,end,t[11];}ac[MAXN*100];
inline void build(char x[])
{
int len=strlen(x+1),now=0;
for(int i=1;i<=len;i++)
{
if(ac[now].t[x[i]-'0']==0)
ac[now].t[x[i]-'0']=++cnt;
now=ac[now].t[x[i]-'0'];
}
ac[now].end=1;
}
inline void get_fail()
{
queue<int>q;
for(int i=0;i<=9;i++)
if(ac[0].t[i]!=0)
ac[ac[0].t[i]].fail=0,q.push(ac[0].t[i]);
while(!q.empty())
{
int u=q.front();q.pop();
ac[u].end|=ac[ac[u].fail].end;
for(int i=0;i<=9;i++)
{
if(ac[u].t[i]!=0)
{
ac[ac[u].t[i]].fail=ac[ac[u].fail].t[i];
q.push(ac[u].t[i]);
}
else ac[u].t[i]=ac[ac[u].fail].t[i];
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%s",N+1);
lenth=strlen(N+1);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
build(s);
}
get_fail();
dp[3][0][0]=dp[2][0][0]=1;
for(int i=0;i<lenth;i++)
{
for(int j=0;j<=cnt;j++)
{
if(ac[j].end!=0) continue;
for(int k=0;k<=9;k++)
{
if(!ac[ac[j].t[k]].end)
{
if(i==0&&k==0) continue;
dp[3][i+1][ac[j].t[k]]=(dp[3][i+1][ac[j].t[k]]+dp[3][i][j])%mod;
}
}
}
}
for(int i=0;i<lenth;++i)
for(int u=0;u<=cnt;++u)
if(!ac[u].end)
{
for(int k=0;k<=9;++k)
if(!ac[ac[u].t[k]].end)
{
if(!i&&!k) continue;
(dp[1][i+1][ac[u].t[k]]+=dp[1][i][u])%=mod;
if(k<N[i+1]-48)(dp[1][i+1][ac[u].t[k]]+=dp[2][i][u])%=mod;
if(k==N[i+1]-48)(dp[2][i+1][ac[u].t[k]]+=dp[2][i][u])%=mod;
}
}
for(int i=1;i<lenth;i++)
for(int j=0;j<=cnt;j++)
ans=(ans+dp[3][i][j])%mod;
for(int i=0;i<=cnt;i++)
ans=(ans+dp[1][lenth][i])%mod,ans=(ans+dp[2][lenth][i])%mod;
printf("%lld\n",ans);
}