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))