问题描述
就说现在拿到了一个由数字字符和 ? 组成的字符串,目标是将所有的 ? 替换成数字字符,使得替换后的字符串表示的十进制整数成为正整数p的倍数。由于方案数可能非常大,需要对最终的结果取模10^9 + 7。
测试样例
样例1:
输入:
s = "??",p = 1输出:100
样例2:
输入:
s = "????1",p = 12输出:0
样例3:
输入:
s = "1??2",p = 3输出:34
题目思路
这是一个动态规划(Dynamic Programming, DP)问题。动态规划是一种通过将复杂问题分解成更简单的子问题来解决的方法。首先我们需要将字符串s中的所有?替换成0-9之间的数字,使得最终的字符串表示的整数是p的倍数。
状态定义:使用一个二维数组dp,其中dp[i][r]表示字符串s的前i个字符替换后,当前数字的余数为r(模p)的方案数。
状态转移:对于每个字符,如果它是?,则可以替换成0-9中的任意一个数字,我们需要更新dp数组,考虑所有可能的替换。如果它是具体的数字,则直接更新dp数组,只考虑这一个数字。
初始状态:dp[0][0] = 1,表示在字符串开始时,余数为0的方案数为1。
边界条件:如果字符串s中的某个字符是?,则它可以被替换成0-9中的任意一个数字,我们需要遍历这10个数字,并更新dp数组。
最终结果:遍历完整个字符串后,dp[n][0](其中n是字符串s的长度)将包含所有可能的方案数,使得字符串s表示的整数是p的倍数。
算法设计
初始化:创建一个大小为(n+1) x p的二维数组dp,并将dp[0][0]设置为1。
遍历字符串:对于字符串s中的每个字符s[i],遍历所有可能的余数r(从0到p-1)。
处理? :如果s[i]是?,则对于0-9中的每个数字digit,计算新的余数new_r = (r * 10 + digit) % p,并更新dp[i+1][new_r]。
处理数字:如果s[i]是具体的数字,则直接计算新的余数new_r = (r * 10 + digit) % p,并更新dp[i+1][new_r]。
取模操作:在更新dp数组时,每次加法操作后都要对结果取模10^9 + 7,以防止整数溢出。
返回结果:遍历结束后,dp[n][0]就是最终的结果,表示所有可能的方案数。
代码详解
def solution(s: str, p: int) -> int:
MOD = 10**9 + 7
n = len(s)
# 初始化 dp 数组
dp = [[0] * p for _ in range(n + 1)]
dp[0][0] = 1
# 遍历字符串 s
for i in range(n):
for r in range(p):
if dp[i][r] > 0:
if s[i] == '?':
# 如果是 '?',可以替换成 0-9 中的任意一个数字
for digit in range(10):
# 更新 dp 数组
# 这里需要计算新的余数
new_r = (r * 10 + digit) % p
dp[i + 1][new_r] = (dp[i + 1][new_r] + dp[i][r]) % MOD
else:
# 如果是数字,直接更新 dp 数组
digit = int(s[i])
new_r = (r * 10 + digit) % p
dp[i + 1][new_r] = (dp[i + 1][new_r] + dp[i][r]) % MOD
# 最终结果为 dp[n][0]
return dp[n][0]
if __name__ == '__main__':
print(solution("??", 1) == 100)
print(solution("????1", 12) == 0)
print(solution("1??2", 3) == 34)
以上代码实现了上述算法。它使用了一个二维数组dp来存储中间结果,并在遍历字符串的过程中更新这个数组。代码中的for循环负责遍历字符串和可能的余数,if语句处理了字符是?和具体数字的两种情况。最后,返回dp[n][0]作为结果。
这个算法的时间复杂度是O(n * p * 10),其中n是字符串的长度,p是给定的倍数。这是因为我们需要遍历每个字符,对于每个字符,我们可能需要遍历p个余数和10个可能的数字替换。空间复杂度是O(n * p),用于存储dp数组。