给你一个长度为50的数字串,问你有多少个子序列构成的数字可以被3整除
答案对1e9+7取模
输入描述:
输入一个字符串,由数字构成,长度小于等于50
输出描述:
输出一个整数
示例1
输入
复制
132
输出
复制
3
示例2
输入
复制
9
输出
复制
1
示例3
输入
复制
333
输出
复制
7
示例4
输入
复制
123456
输出
复制
23
示例5
输入
复制
00
输出
复制
3
备注:
n为长度
子任务1: n <= 5
子任务2: n <= 20
子任务3: 无限制
思路:
易知,若要被三整除必须各位数字之和为3的倍数。
可以将原问题分解为子问题的和进行解决,从底自上,将字符串的数字都对三求余转化为0 、1、2。
设置一个dp数组存储前 i 位数字之和有几种情况对3求余为0,1,或2。
切可以发现,当输入第 i 个数时 (1) 若 该数对3求余 == 0 那么前 i 个数中对3 求余为0 的组合为 dp[0] + dp[0] + 1,其余两种情况分别加dp[1],dp[2];(2) 若 %3 = = 1,那么前 i 个数中对3 求余为0 的组合为 dp[1] + dp[0] +1,可以理解为,情况1,保留原先%3 = = 1的组合数,即dp[1] ,但是将第 i 位数字与%3 = = 0 的原组合进行组合,可以得到dp[0] 数量的%3 = = 1的组合,同时dp[0]的数量也会随之更新为dp[2] ,即dp[2] 数量的%3 = =2 的组合与1进行组合得到dp[2]+dp[0]数量的满足%3 = =0的子序列,同理dp[2]的增加量为dp[1] (3)由(1)(2)得 当 第 i 位数字%3 = = 2 时,直接可得 dp[0]增量为 dp[1],dp[1]增量为 dp[2],dp[2]增量为 dp[0] +1。
故可以得到如下代码`
#include <iostream>
#include <string>
#include <string.h>
#define ll long long
using namespace std;
const int Mod = 1e9 + 7;
int main()
{
string mathstr;
cin>>mathstr;
ll dp[3];
memset(dp,0,sizeof(dp));
for(int i = 0; i < mathstr.size(); i++)
{
int s0 = 0,s1 = 0,s2 = 0;//这步不能省略,因为如果省略导致第一步增加后得到的dp在下一步别的dp增加时,导致错误。
if((mathstr[i] - '0') % 3 == 0)
{
s0 += dp[0] + 1;
s1 += dp[1];
s2 += dp[2];
}
if((mathstr[i] - '0') % 3 == 1)
{
s0 += dp[2];
s1 += dp[0] + 1;
s2 += dp[1];
}
if((mathstr[i] - '0') % 3 == 2)
{
s0 += dp[1];
s1 += dp[2];
s2 += dp[0] + 1;
}
dp[0] += s0;
dp[1] += s1;
dp[2] += s2;
for(int j = 0; j < 3; j++)//求余
{
dp[j] %= Mod;
}
}
cout<< dp[0] <<endl;
return 0;
}