[51nod 1587]半现串

题目描述

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1587

AC自动机上的DP

把S所有长度是d/2的提出来造个AC自动机。
然后做数位DP,用一维状态表示匹配到AC自动机上哪个节点。
匹配成功状态不要转移。
于是可以计算出不匹配的数量。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxtot=30000+10,mo=1000000007;
char s[1200],h[1200];
int g[maxtot][10],next[maxtot][10],fail[maxtot],a[1000],b[1000],c[1000],d[1000],dl[maxtot];
bool bz[maxtot],czy;
int f[60][maxtot];
int i,j,k,l,r,t,n,m,tot,root,head,tail,ans,num;
char ch;
char get(){
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    return ch;
}
void insert(int &x,int y){
    if (!x) x=++tot;
    if (y>m/2){
        bz[x]=1;
        return;
    }
    insert(g[x][h[y]-'0'],y+1);
}
void prepare(){
    dl[tail=1]=root;
    fail[root]=root;
    fo(i,0,9) next[root][i]=root;
    while (head<tail){
        k=dl[++head];
        bz[k]|=bz[fail[k]];
        fo(i,0,9)
            if (g[k][i]) next[k][i]=g[k][i];else next[k][i]=next[fail[k]][i];
        fo(i,0,9)
            if (g[k][i]){
                if (k==root){
                    dl[++tail]=g[k][i];
                    fail[g[k][i]]=root;
                    continue;
                }
                j=fail[k];
                while (j!=root&&!g[j][i]) j=fail[j];
                if (g[j][i]) j=g[j][i];
                dl[++tail]=g[k][i];
                fail[g[k][i]]=j;
            }
    }
}
void calc(int fh){
    fo(i,0,m)
        fo(j,0,tot)
            f[i][j]=0;
    czy=1;
    t=root;
    fo(i,0,m-1){
        if (czy)
            fo(j,0,b[i+1]-1) (f[i+1][next[t][j]]+=1)%=mo;
        t=next[t][b[i+1]];
        if (bz[t]) czy=0;
        fo(j,1,tot){
            if (bz[j]) continue;
            fo(k,0,9)
                (f[i+1][next[j][k]]+=f[i][j])%=mo;
        }
    }
    if (czy) (f[m][t]+=1)%=mo;
    fo(i,1,tot)
        if (!bz[i]) (ans+=f[m][i]*fh)%=mo;
}
int main(){
    //freopen("data.in","r",stdin);//freopen("wzd.out","w",stdout);
    scanf("%s",s+1);
    n=strlen(s+1);
    a[m=1]=get()-'0';
    while (1){
        ch=getchar();
        if (ch<'0'||ch>'9') break;
        a[++m]=ch-'0';
    }
    fo(i,1,m) b[i]=get()-'0';
    fo(i,1,m) c[i]=a[i],d[i]=b[i];
    t=1;
    fd(i,m,1){
        if (d[i]<c[i]){
            d[i]+=10;
            d[i-1]--;
        }
        (num+=(ll)t*(d[i]-c[i])%mo)%=mo;
        t=(ll)t*10%mo;
    }
    (num+=1)%=mo;
    root=tot=1;
    fo(i,1,n-(m/2)+1){
        fo(j,i,i+(m/2)-1) h[j-i+1]=s[j];
        insert(root,1);
    }
    prepare();
    calc(1);
    fo(i,1,m) b[i]=a[i];
    b[m]--;
    i=m;
    while (b[i]<0){
        b[i]+=10;
        b[i-1]--;
        i--;
    }
    calc(-1);
    (num-=ans)%=mo;
    (num+=mo)%=mo;
    printf("%d\n",num);
    /*fo(i,1,tot){
        fo(j,0,9) printf("%d ",next[i][j]);
        printf("\n");
    }*/
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值