【JZOJ3624】【SDOI2014】数数(count) AC自动机+数位dp

题面

1146820-20170528082336016-111782503.png
1146820-20170528082344907-1723519379.png

100

容易想到使用AC自动机来处理禁忌子串的问题;
然后在自动机上数位dp,具体是:
\(f_{i,j,0/1}\)表示填了\(i\)位,当前在自动机的第\(j\)个结点上,\(0\)表示当前数已经小于了\(N\)\(1\)表示当前数等于\(N\)

Code

#include<bits/stdc++.h>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;i++)
#define fd(i,x,y) for(int i=x;i>=y;i--)
using namespace std;
const char* fin="ex3624.in";
const char* fout="ex3624.out";
const int inf=0x7fffffff;
const int maxn=1507,mo=1000000007;
struct node{
    int x,fa,ne[10];
}ac[maxn];
int n,m,num=1,hd,tl=1;
int f[maxn][maxn][2],ne[maxn][10],b[maxn];
char a[maxn];
void makefail(){
    while (hd++<tl){
        int x=b[hd];
        fo(i,0,9)
            if (ac[x].ne[i]){
                int y=ac[x].ne[i],z=ac[x].fa;
                if (x){
                    while (z && !ac[z].ne[i]) z=ac[z].fa;
                    ac[y].fa=ac[z].ne[i];
                }
                b[++tl]=y;
                ac[y].x+=ac[ac[y].fa].x;
            }
    }
}
int main(){
    freopen(fin,"r",stdin);
    freopen(fout,"w",stdout);
    scanf("%s",a+1);
    n=strlen(a+1);
    scanf("%d",&m);
    fo(i,1,m){
        char ch=getchar();
        int p=0;
        while (ch<'0' || ch>'9') ch=getchar();
        while (ch>='0' && ch<='9'){
            int x=ch-'0';
            if (!ac[p].ne[x]) ac[p].ne[x]=++num;
            p=ac[p].ne[x];
            ch=getchar();
        }
        ac[p].x++;
    }
    makefail();
    fo(i,0,num){
        if (i==1){
            ne[1][0]=1;
            fo(j,1,9) ne[1][j]=ne[0][j];
            continue;
        }
        fo(j,0,9){
            int x=ac[i].ne[j],y=ac[i].fa;
            if (x){
                if (ac[x].x) ne[i][j]=-1;
                else ne[i][j]=x;
            }else{
                while (y && !ac[y].ne[j]) y=ac[y].fa;
                if (ac[ac[y].ne[j]].x) ne[i][j]=-1;
                else ne[i][j]=ac[y].ne[j];
            }
        }
    }
    memset(f,0,sizeof f);
    f[0][1][0]=1;
    fo(i,0,n-1)
        fo(x,0,num){
            fo(y,0,9){
                if (ne[x][y]==-1) continue;
                int z=ne[x][y],xc=a[i+1]-'0';
                if (y<=xc){
                    if (y<xc) f[i+1][z][1]=(f[i+1][z][1]+f[i][x][0])%mo;
                    else f[i+1][z][0]=(f[i+1][z][0]+f[i][x][0])%mo;
                }
                f[i+1][z][1]=(f[i+1][z][1]+f[i][x][1])%mo;
            }
        }
    int ans=0;
    fo(x,0,num) ans=((ans+f[n][x][0])%mo+f[n][x][1])%mo;
    ans--;
    printf("%d",ans);
    return 0;
}

转载于:https://www.cnblogs.com/hiweibolu/p/6914986.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值