被3整除的子序列(动态规划)

1. 问题描述:

给你一个长度为50的数字串,问你有多少个子序列构成的数字可以被3整除,答案对1e9+7取模

输入描述:
输入一个字符串,由数字构成,长度小于等于50
输出描述:
输出一个整数
示例1
输入
复制
132
输出
3
示例2
输入
9
输出
1
示例3
输入
333
输出
7
示例4
输入
123456
输出
23
示例5
输入
00
输出
3

链接:https://ac.nowcoder.com/acm/problem/21302
来源:牛客网

2. 思路分析:

对于子序列的相关问题我们都可以尝试使用动态规划的思路解决,对于这道题目来说思路也是类似的,而且往往子序列的相关的问题都考虑以当前的数字nums[i]结尾的....的思路。因为涉及到两个动态变化的参数,第一个参数是以当前的数字i结尾,第二个参数是以当前数字结尾的余数,所以我们可以声明一个二维的dp列表(c/c++/java为二维数组),dp[i][j]表示以当前数字nums[i]]结尾的余数为j的数目(j = 0,1,2),明确dp列表的含义之后接下来的就比较简单了,具体的思路是尝试将当前的数字nums[i]添加到之前序列的后面也可以自己作为一个序列所以存在两种状态。首先需要知道的一点是例如两位数字13对3取余,增加多一位数字6也即136对3取余结尾那么最终的余数为(13 %3 + 6 % 3)% 3 = 1,当我们尝试将当前的数字nums[i]添加到之前余数为0,1,2的序列的时候,如果当前数字对3取余的余数为0那么添加到之前的序列之后最终的余数还是为0,如果当前数字对3取余的余数为1那么添加到之前的序列有三种情况:一是添加到余数为0的序列后面最终余数为1,添加到余数为1的序列后面那么余数为2,添加到余数为2的序列后面那么余数为0....以此类推。对于当前的数字可以尝试添加到之前的序列后面也可以不添加之前的序列后面所以存在两种情况,分情况讨论即可。

3. 代码如下:

if __name__ == '__main__':
    s = input()
    dp = [[0] * 3 for i in range(len(s))]
    # 初始化第一个位置对应的余数的dp值
    dp[0][int(s[0]) % 3] = 1
    for i in range(1, len(s)):
        reminder = int(s[i]) % 3
        # 自己就是一个余数为reminder的序列
        dp[i][reminder] = 1
        # 下面的三个判断是尝试将当前的数字添加到之前余数为0/1/2序列的后面
        if reminder == 0:
            # 可以添加到之前余数为0/1/2序列的后面也可以不加所以总的情况需要乘以2
            dp[i][0] += dp[i - 1][0] * 2
            dp[i][1] += dp[i - 1][1] * 2
            dp[i][2] += dp[i - 1][2] * 2
        elif reminder == 1:
            # 当前余数为1那么则需要加上余数为2的才可以构成以当前位置i对应的数字结尾的余数为0的序列
            # 其余的情况也是类似的
            dp[i][0] += dp[i - 1][0] + dp[i - 1][2]
            dp[i][1] += dp[i - 1][0] + dp[i - 1][1]
            dp[i][2] += dp[i - 1][1] + dp[i - 1][2]
        else:
            dp[i][0] += dp[i - 1][0] + dp[i - 1][1]
            dp[i][1] += dp[i - 1][1] + dp[i - 1][2]
            dp[i][2] += dp[i - 1][2] + dp[i - 1][0]
    print(dp[len(s) - 1][0] % (10 ** 9 + 7))

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一个关于去除序列中包含数字3的题目。给你一个长度为50的数字串,问你有多少个序列构成的数字串可以被3整除。答案为1e9+7取模。 输入描述: 输入一个字符串,由0-9组成,长度小于等于50。 输出描述: 输出一个整数,表示答案。 输入一个长度为50的数字字符串,由数字组成。让你输出里面去掉序列包含数字3的数字串的长度。举个例,输入132,因为包含3,所以去掉{3,13,32,132}四个序列,去掉后只剩下1个序列2,所以输出3。 ### 回答2: 本题可以使用动态规划的思路来解决。我们可以使用一个二维数组dp[i][j]表示以第i个数字结尾的长度为j的序列能否被3整除,其中i的范围是1到50,j的范围是1到50。dp[i][j]的值为0或1,0表示不能被3整除,1表示能被3整除。 对于dp[i][j],我们可以通过dp[i-1][j-1]或dp[i-1][j]来转移。如果dp[i-1][j-1]为1,则说明在前面的j-1个数字中有一个序列能被3整除,此时如果第i个数字为0、3、6或9,则dp[i][j]也能被3整除,否则不能;如果dp[i-1][j]为1,则说明在前面的j个数字中有一个序列能被3整除,此时如果第i个数字为1、4或7,则dp[i][j]也能被3整除,否则不能。 最终,我们只需要将dp数组中所有值等于1的元素的个数相加,并将结果对1e9+7取模即可。 以下是完整代码: ### 回答3: 题目描述 给定一个长度为50的数字串,求出其中有多少个序列的和可以被3整除。输出对10^9+7取模的答案。 思路分析 我们可以先将给定的数字串转换成数字数组,将其看做是一个长度为n的数组a。我们令dp[i][0]为a[1]~a[i]中被3整除序列的个数,dp[i][1]为a[1]~a[i]中余1的序列的个数,dp[i][2]为余2的序列的个数。注意我们计算余数时可以直接用数值模3,余数就是该数值除以3的余数。 那么如果我们现在已经求出了dp[i-1][0],dp[i-1][1]和dp[i-1][2],我们如何求dp[i][0],dp[i][1]和dp[i][2]呢?我们考虑当前数值a[i]对于这三种状态的影响: 如果a[i]可以被3整除,那么它可以加入到之前状态为0的所有序列中,我们有dp[i][0] = dp[i-1][0] + 1,同时之前状态为1的序列末尾加入该数后会变成状态为0的序列,同样的,之前状态为2的序列末尾加入该数也会变成状态为1的序列,所以我们还需要加上dp[i-1][1]和dp[i-1][2]; 如果a[i]除以3余1,那么它可以加入到之前状态为2的所有序列中,我们有dp[i][1] = dp[i-1][1] + 1,同时之前状态为0的序列末尾加入该数后会变成状态为1的序列,同样的,之前状态为1的序列末尾加入该数也会变成状态为2的序列,所以我们还需要加上dp[i-1][0]和dp[i-1][2]。 如果a[i]除以3余2,那么同理可以得到dp[i][2]的状态转移方程。 最终我们的答案即为dp[n][0],因为被3整除的数字序列余数为0。同时,我们要注意答案需要对1e9+7取模。 时间复杂度为O(n)。 参考代码 这里给出Java代码实现:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值