[SDOI2014]数数(AC自动机,动态规划)
[SDOI2014]数数(AC自动机,动态规划)
写题解前先泄愤...本来觉得应该像数位dp一样搞一个当前是否还是前导零的一维,由于当时特别困导致调不下去就盯着代码发呆,浪费时间浪费生命[快哭了]
想在这里请诸位查一下我的以前那份错误的代码qwq,也算是为自己留存一下疑惑,以后想得起来继续查。code
题目叙述
给你一个由数字组成的字符串,要求这个字符串的子串中不能出现某些字符串,并且这个字符串组成的数不能比某个给定的数大。
题解
设
转移:
- 从
dp_{i,j,0} 到dp_{i+1,j,0} ,加了一位还是压线,说明新加的位要压线。 - 从
dp_{i,j,0} 到dp_{i+1,j,1} ,加了一位没有压线,说明新加的位不能压线。 - 从
dp_{i,j,1} 到dp_{i+1,j,1} ,这个转移没有限制。
代码
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxLen = 1205, maxNode = 1505, mod = 1e9 + 7;
int ch[maxNode][10], dp[maxLen][maxNode][2], fail[maxNode], tot = 1, nbLim;
bool tag[maxNode];
char big[maxLen], lim[maxLen];
queue<int> Q;
//dp[i][0/1][0/1]表示前 i位,有没有分出大小,当前还是不是前导零
void Insert(char *str) {
int len = strlen(str + 1), now = 1;
for (int pos = 1; pos <= len; ++pos) {
if (!ch[now][str[pos] - '0'])
ch[now][str[pos] - '0'] = ++tot;
now = ch[now][str[pos] - '0'];
}
tag[now] = 1;
}
void GetFail() {
fail[1] = 1;
for (int son = 0; son < 10; ++son) {
if (ch[1][son]) {
fail[ch[1][son]] = 1;
Q.push(ch[1][son]);
} else
ch[1][son] = 1;
}
while (!Q.empty()) {
int now = Q.front();
Q.pop();
for (int son = 0; son < 10; ++son) {
if (ch[now][son]) {
Q.push(ch[now][son]);
fail[ch[now][son]] = ch[fail[now]][son];
} else
ch[now][son] = ch[fail[now]][son];
}
}
}
void Add(int &x, int y) {
x += y, (x > mod) ? x -= mod : x = x;
}
void Minus(int &x, int y) {
x -= y, (x < 0) ? x += mod : x = x;
}
void Check() {
for (int i = 1; i <= tot; ++i) {
printf("node : %d fail : %d\n", i, fail[i]);
for (int son = 0; son < 10; ++son)
if (ch[i][son])
printf(" ch : %d son : %d\n", son, ch[i][son]);
}
}
int main() {
scanf("%s%d", big + 1, &nbLim);
for (int i = 1; i <= nbLim; ++i) {
scanf("%s", lim + 1);
Insert(lim);
}
GetFail();
int bigLen = strlen(big + 1);
dp[0][1][0] = 1;
ch[1][0] = 1;
for (int len = 0; len < bigLen; ++len) {
for (int node = 1; node <= tot; ++node) {
if (tag[node])
continue ;
for (int son = 0; son < 10; ++son) {
if (tag[ch[node][son]])
continue ;
Add(dp[len + 1][ch[node][son]][0], dp[len][node][0] * (son == (big[len + 1] - '0')));
Add(dp[len + 1][ch[node][son]][1], dp[len][node][1]);
Add(dp[len + 1][ch[node][son]][1], dp[len][node][0] * (son < (big[len + 1] - '0')));
}
}
}
int ans = 0;
for (int node = 1; node <= tot; ++node)
Add(ans, dp[bigLen][node][0]), Add(ans, dp[bigLen][node][1]);
Minus(ans, 1);
printf("%d\n", ans);
return 0;
}
知识点
- 困得时候就要立刻睡觉!!!(尤其疫情期间,有条件这样做。。。