题目描述
给你一个长度为50的数字串,问你有多少个子序列构成的数字可以被3整除
答案对1e9+7取模
输入描述:
输入一个字符串,由数字构成,长度小于等于50
输出描述:
输出一个整数
示例1
输入
132
输出
3
示例2
输入
9
输出
1
示例3
输入
333
输出
7
示例4
输入
123456
输出
23
示例5
输入
00
输出
3
题目分析
❗ 子序列可以不连续
❗ 被3整除即每一位上的数字相加之和为3的倍数
代码
未优化的DP
#include <bits/stdc++.h>
using namespace std;const int N = 55, MOD = 1e9+7;
char s[N];
int f[N][3]; //根据余数可分成3种情况
//前i位余数为0、1、2,再根据当前第i位的余数为0、1、2的情况,求解递推
//f(i, j)表示前i位中,序列之和余数为j的子序列个数。
//每次分两种情况讨论:(1)不选第i位 (2)选第i位int main(){cin >> s+1;int n = strlen(s+1);// f[0][0] = f[0][1] = f[0][2] = 0;for(int i = 1; i <= n; ++i){int ys = (s[i]-'0')%3; //第i位余数if(ys == 0){f[i][0] = (f[i-1][0] + f[i-1][0]+1) % MOD; //+1是因为可以只选第i位f[i][1] = (f[i-1][1] + f[i-1][1]) % MOD;f[i][2] = (f[i-1][2] + f[i-1][2]) % MOD;}else if(ys == 1){f[i][0] = (f[i-1][0] + f[i-1][2]) % MOD;f[i][1] = (f[i-1][1] + f[i-1][0]+1) % MOD;f[i][2] = (f[i-1][2] + f[i-1][1]) % MOD;}else{ //ys == 2f[i][0] = (f[i-1][0] + f[i-1][1]) % MOD;f[i][1] = (f[i-1][1] + f[i-1][2]) % MOD;f[i][2] = (f[i-1][2] + f[i-1][0]+1) % MOD;}}cout << f[n][0] << endl;return 0;
}
优化后的DP
边读入边处理,同时将二维数组f降维为一维数组f
#include <bits/stdc++.h>
using namespace std;const int N = 55, MOD = 1e9+7;
int f[3];
int main(){char s;while(scanf("%c", &s) != EOF && s != '\n'){int ys = (s-'0')%3; //第i位余数int a = f[0], b = f[1], c = f[2];if(ys == 0){f[0] = (a + a+1) % MOD; //+1是因为可以只选第i位f[1] = (b + b) % MOD;f[2] = (c + c) % MOD;}else if(ys == 1){f[0] = (a + c) % MOD;f[1] = (b + a+1) % MOD;f[2] = (c + b) % MOD;}else{ //ys == 2f[0] = (a + b) % MOD;f[1] = (b + c) % MOD;f[2] = (c + a+1) % MOD;}}cout << f[0] << endl;return 0;
}