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);
}

posted @ 2019-02-17 21:47  风浔凌  阅读(293)  评论(0编辑  收藏  举报