牛客21302 被三整除的子序列(dp)

给你一个长度为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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值