链接:https://ac.nowcoder.com/acm/problem/21302
来源:牛客网
题目:
给你一个长度为50的数字串,问你有多少个子序列构成的数字可以被3整除
答案对1e9+7取模
输入:
输入一个字符串,由数字构成,长度小于等于50
输出:
输出一个整数
分析:
对一个数字字符串取余有:
123456789 % m =(((1 * 10 + 2) % m * 10 + 3) % m * 10+ 4) % m......
定义dp[pos][0/1][mod],表示第 pos 位 取/不取 时余数为 mod 时的子序列个数,mod属于【0,2】
有如下转移方程:
(1)不取pos位
dp[pos][0][mod] = dp[pos-1][0][mod] + dp[pos-1][1][mod]
(2)取第pos位:
dp[pos][1][mod] = dp[pos-1][0][k] + dp[pos-1][1][k]
K应该满足:(K * 10 % 3 + a[pos])% 3 = mod,暴力K的范围[0,2],满足条件时就转移
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD = 1e9+7;
LL dp[60][2][3],a[60];
char s[60];
int main()
{
scanf("%s",s+1);
int len = strlen(s+1);
for(int i = 1; i <= len; ++i)
{
a[i] = (s[i] - '0') % 3;
}
dp[0][0][0] = 1; //开始时都不选且余数为0 = 1
for(int i = 1 ; i <= len; ++i)
{
for(int j = 0;j < 3; ++j)
{
dp[i][0][j] = (dp[i-1][0][j] + dp[i-1][1][j]) % MOD;
for(int k = 0; k < 3 ; ++k)
{
if((k+a[i]) % 3 == j)
dp[i][1][j] = (dp[i-1][0][k] + dp[i-1][1][k]) % MOD;
}
}
}
cout<<(dp[len][1][0] + dp[len][0][0] - 1 + MOD) % MOD; //减去全不选的情况
return 0;
}