• 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
    ",ans);
    }
    
    
  • 相关阅读:
    zoj 2913 Bus Pass
    poj 2478 Farey Sequence
    zoj 1649 Rescue
    秒懂JavaScript HTML DOM 元素 (节点)
    看了就会的JS(JavaScript)addEventListener()
    秒懂javascript的原型(prototype)对象、原型链的前世今生
    构造函数用途及优缺点
    一文读懂 js(JavaScript)中call() 和 apply() 的用法
    js (JavaScript)函数声明的几种形式及用法
    JavaScript 代码规范
  • 原文地址:https://www.cnblogs.com/fengxunling/p/10392837.html
Copyright © 2020-2023  润新知