一、小U的问号替换问题
问题描述
小C拿到了一个由数字字符和 ? 组成的字符串,她的目标是将所有的 ? 替换成数字字符,使得替换后的字符串表示的十进制整数成为正整数p的倍数。由于方案数可能非常大,需要对最终的结果取模 10^9 + 7。
测试样例
样例1
输入:
s = "??",p = 1
输出:100
样例2
输入:
s = "????1",p = 12
输出:0
样例3
输入:
s = "1??2",p = 3
输出:34
二、解题思路
(1)问题理解
首先,我们需要理解问题的核心要求:给定一个由数字字符和 ? 组成的字符串 s,我们需要将所有的 ? 替换成数字字符,使得替换后的字符串表示的十进制整数是正整数 p 的倍数。由于方案数可能非常大,我们需要对最终的结果取模 10^9 + 7。
(2)数据结构的选择
为了解决这个问题,我们可以使用动态规划(Dynamic Programming, DP)。动态规划是一种通过将问题分解为子问题并存储子问题的解来解决复杂问题的方法。在这个问题中,我们可以定义一个状态 dp[i][r],表示前 i 个字符组成的字符串,其模 p 的余数为 r 的方案数。
(3)算法步骤
1.初始化
我们首先初始化一个二维数组 dp,其中 dp[i][r] 表示前 i 个字符组成的字符串,其模 p 的余数为 r 的方案数。
初始状态 dp[0][0] = 1,表示空字符串模 p 的余数为 0 的方案数为 1。
2.状态转移
遍历字符串 s 的每个字符 s[i]。
对于每个字符 s[i],我们需要考虑两种情况:
1.如果 s[i] 是 ?,则它可以被替换为 0-9 中的任意一个数字。
2.如果 s[i] 是数字字符,则它保持不变。
对于每种情况,我们需要计算新的余数 new_r,并更新 dp 数组。
3.计算新的余数
对于每个字符 s[i],如果它是 ?,则我们需要尝试将其替换为 0-9 中的任意一个数字。
新的余数 new_r 可以通过以下公式计算:new_r = (r * 10 + digit) % p,其中 r 是前 i-1 个字符组成的字符串的余数,digit 是当前字符替换后的数字。
4.更新dp数组
对于每个可能的 digit,我们更新 dp[i + 1][new_r],即前 i+1 个字符组成的字符串,其模 p 的余数为 new_r 的方案数。
由于方案数可能非常大,我们需要在每次更新 dp 数组时对 10^9 + 7 取模。
5.最终结果
最终答案为 dp[len(s)][0],即整个字符串模 p 的余数为 0 的方案数。
三、完整代码
def solution(s: str, p: int) -> int:
MOD = 10**9 + 7
n = len(s)
# dp[i][r] 表示前 i 个字符组成的字符串,其模 p 的余数为 r 的方案数
dp = [[0] * p for _ in range(n + 1)]
dp[0][0] = 1 # 空字符串模 p 的余数为 0 的方案数为 1
for i in range(n):
for r in range(p):
if dp[i][r] > 0:
if s[i] == '?':
for digit in range(10):
new_r = (r * 10 + digit) % p
dp[i + 1][new_r] = (dp[i + 1][new_r] + dp[i][r]) % MOD
else:
digit = int(s[i])
new_r = (r * 10 + digit) % p
dp[i + 1][new_r] = (dp[i + 1][new_r] + dp[i][r]) % MOD
return dp[n][0]
if __name__ == '__main__':
print(solution("??", 1) == 100)
print(solution("????1", 12) == 0)
print(solution("1??2", 3) == 34)
四、总结
通过使用动态规划,我们可以有效地解决这个问题。我们定义了一个状态 dp[i][r],表示前 i 个字符组成的字符串,其模 p 的余数为 r 的方案数。通过遍历字符串 s,并根据当前字符是 ? 还是数字字符,我们更新 dp 数组。最终答案为 dp[len(s)][0],即整个字符串模 p 的余数为 0 的方案数。