3530: [Sdoi2014]数数
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 1411 Solved: 723
[Submit][Status][Discuss]
Description
我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串。例如当S=(22,333,0233)时,233是幸运数,2333、20233、3223不是幸运数。
给定N和S,计算不大于N的幸运数个数。
Input
输入的第一行包含整数N。
接下来一行一个整数M,表示S中元素的数量。
接下来M行,每行一个数字串,表示S中的一个元素。
Output
输出一行一个整数,表示答案模109+7的值。
Sample Input
20
3
2
3
14
Sample Output
14
HINT
下表中l表示N的长度,L表示S中所有串长度之和。
1 < =l < =1200 , 1 < =M < =100 ,1 < =L < =1500
用0 1 2 三种状态表示前 i 位 小于, 等于, 大于 上限的情况
枚举第 i + 1 位, 转移与上限的比较情况, 统计各种情况的总数
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int ml = 1210, mm = 103, mL = 1510;
const int mod = 1e9 + 7;
int num;
int nx[mL][10], fail[mL];
bool ed[mL];
void add(char a[])
{
int len = strlen(a);
int r = 0;
for (int i = 0; i < len; i++)
{
if (nx[r][a[i] - '0'] == -1)
nx[r][a[i] - '0'] = ++num;
r = nx[r][a[i] - '0'];
}
ed[r] = 1;
}
void build_Fail()
{
queue<int>que;
int root = 0;
fail[root] = root;
for (int i = 0; i < 10; i++)
{
if (nx[root][i] == -1)
nx[root][i] = root;
else
{
fail[nx[root][i]] = root;
que.push(nx[root][i]);
}
}
while (!que.empty())
{
int now = que.front();
que.pop();
for (int i = 0; i < 10; i++)
{
if (nx[now][i] == -1)
nx[now][i] = nx[fail[now]][i];
else
{
fail[nx[now][i]] = nx[fail[now]][i];
que.push(nx[now][i]);
}
}
if (ed[fail[now]])
ed[now] = 1;
}
}
ll dp[ml][mL][3];
ll DP(char ch[])
{
ll ans = 0;
int len = strlen(ch);
for (int k = 1; k < 10; k++)
{
if (ed[nx[0][k]])
continue;
int p;
if (k < ch[0] - '0')
p = 0;
else if (k == ch[0] - '0')
p = 1;
else if (k > ch[0] - '0')
p = 2;
dp[0][nx[0][k]][p]++;
}
for (int j = 0; j <= num; j++)
for (int k = 0; k < ((len > 1) ? 3 : 2); k++)
(ans += dp[0][j][k]) %= mod;
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j <= num; j++)
{
for (int k = 0; k < 10; k++) /// 枚举下一位
{
int p; /// 与上限的比较情况
if (k < ch[i + 1] - '0')
p = 0;
else if (k == ch[i + 1] - '0')
p = 1;
else if (k > ch[i + 1] - '0')
p = 2;
int t = nx[j][k];
if (ed[t])
continue;
(dp[i + 1][t][0] += dp[i][j][0]) %= mod;
(dp[i + 1][t][p] += dp[i][j][1]) %= mod;
(dp[i + 1][t][2] += dp[i][j][2]) %= mod;
}
}
for (int j = 0; j <= num; j++)
{
for (int q = 0; q < 3; q++)
{
if (i + 1 == len - 1 && q == 2) /// 位数到达上限, 总值超过上限
break;
(ans += dp[i + 1][j][q]) %= mod;
}
}
}
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("D:\\in.txt", "r", stdin);
#endif // ONLINE_JUDGE
memset(nx, -1, sizeof nx);
char n[ml];
scanf("%s", n);
int m;
scanf("%d", &m);
while (m--)
{
char ch[ml];
scanf("%s", ch);
add(ch);
}
build_Fail();
printf("%lld\n", DP(n));
return 0;
}