[BZOJ3530] [Sdoi2014]数数 && AC自动机+dp

6 篇文章 0 订阅
4 篇文章 0 订阅

首先要建立AC自动机 然后模板串可能前面出现0 处理起来可能会有点麻烦 所以我们干脆直接把模板串倒着插入 然后在Trie中进行dp

用d[i][j][0] 表示在第i个节点 枚举到第j位 且第j位没有超过N的方案数(d[i][j][1]则为超过了

至于为什么要统计超过了的数的个数 是因为我们是从低位开始开始确定这个数的 所以之后的位数如果小于N 那么整体是不会超过N的)

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
#define SF scanf
#define PF printf
#define idx(c) (c-'0')
using namespace std;
typedef long long LL;
const int MAXNODE = 1500;
const int SZ = 10;
const int MOD = 1e9+7;
char s[MAXNODE+10], ss[MAXNODE+10];
int n;
int d[MAXNODE+10][MAXNODE+10][2+10];
struct Aho_Corasick {
	int ch[MAXNODE+10][SZ+10], match[MAXNODE+10], f[MAXNODE+10];
	int ncnt, zero[MAXNODE+10];
	void init() { ncnt = 1; memset(ch[0], 0, sizeof(ch[0])); }
	int NewNode() { memset(ch[ncnt], 0, sizeof(ch[ncnt])); return ncnt++; }
	void insert(char *s) {
		int n = strlen(s), u = 0;
		for(int i = n-1; i >= 0; i--) {
			int c = idx(s[i]);
			if(!ch[u][c]) ch[u][c] = NewNode();
			u = ch[u][c];
		}
		match[u] = 1;
		if(s[0] == '0') zero[u] = 1;
	}
	void GetFail() {
        queue<int> q;
        f[0] = 0;
        for(int i = 0; i < SZ; i++) {
            if(!ch[0][i]) ch[0][i] = 0;
            else { 
                q.push(ch[0][i]);
                f[ch[0][i]] = 0;
            }
        }
        while(!q.empty()) {
            int u = q.front(); q.pop();
            for(int i = 0; i < SZ; i++) {
                if(!ch[u][i]) { ch[u][i] = ch[f[u]][i]; continue; }
                q.push(ch[u][i]);
                f[ch[u][i]] = ch[f[u]][i];
				match[ch[u][i]] |= match[f[ch[u][i]]];
				zero[ch[u][i]] |= zero[ch[u][i]];
            }
        }
    }
	void dp(int u, int cur, int over) {
		int &ret = d[u][cur][over];
		if(~ret) return ;
		if(match[u]) {
			int t = 0;
			if(zero[u]) t = 1;
			d[u][cur][0] = d[u][cur][1] = t;
			if(!cur) d[u][cur][1] = 0;
			return ;
		}
		if(!cur) {
			d[u][cur][1] = 0; d[u][cur][0] = 1;
			return ;
		}
		ret = 0;
		for(int c = 0; c < SZ; c++) {
			if(over) {
				if(c >= idx(s[cur-1])) {
					dp(ch[u][c], cur-1, 1);
					ret = (ret + d[ch[u][c]][cur-1][1]) % MOD;
				}
				else {
					dp(ch[u][c], cur-1, 0);
					ret = (ret + d[ch[u][c]][cur-1][0]) % MOD;
				}
			}
			else {
				if(c > idx(s[cur-1])) {
					dp(ch[u][c], cur-1, 1);
					ret = (ret + d[ch[u][c]][cur-1][1]) % MOD;
				}
				else {
					dp(ch[u][c], cur-1, 0);
					ret = (ret + d[ch[u][c]][cur-1][0]) % MOD;
				}
			}
		}
	}
} ac;
int main() {
	ac.init();
	memset(d, -1, sizeof(d));
	SF("%s%d", s, &n);
	for(int i = 1; i <= n; i++) {
		SF("%s", ss); 
		ac.insert(ss);
	}
	ac.GetFail();
	int len = strlen(s);
	ac.dp(0, len, 0);
	PF("%d\n", d[0][len][0]-1);
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值