问题描述
小C拿到了一个由数字字符和 ? 组成的字符串,她的目标是将所有的 ? 替换成数字字符,使得替换后的字符串表示的十进制整数成为正整数 的倍数。由于方案数可能非常大,需要对最终的结果取模 。
测试样例
样例1:
输入:
s = "??",p = 1
输出:100
样例2:
输入:
s = "????1",p = 12
输出:0
样例3:
输入:
s = "1??2",p = 3
输出:34
问题分析
我们考虑没有?的情况去判断一个数是不是的倍数,我们可以直接使用高精度模拟的方法,从前往后去计算每位数字对取模的结果,然后乘以加到下一位上直到各位,然后,判断他是不是恰好对取模后为0。
现在我们考虑加上问号的情况,对于每个问号,我们都有0~9一共10种不同的可能取值,由于我们需要计算所有可能的替换方案,并且需要判断这些方案是否是 p 的倍数,动态规划是一个合适的选择。
动态规划
状态定义:我们可以定义一个二维数组 dp,其中 dp[i][j] 表示前 i 个字符组成的数字模 p 余 j 的方案数。
初始化:
dp[0][0] = 1,表示空字符串模p余0的方案数为 1。- 其他
dp[0][j] = 0,表示空字符串模p余其他值的方案数为 0。
状态转移:
- 对于每个字符
s[i],如果是数字字符,则直接计算其对p的余数,并更新dp数组。 - 如果是
?,则需要考虑所有可能的数字(0-9),并分别计算其对p的余数,更新dp数组。
务必注意在转移过程中,需要实时对 取模,否则方案数会很快超出long long存储的上限。
结果:
- 最终结果为
dp[n][0],其中n是字符串的长度,表示整个字符串模p余0的方案数。
复杂度分析
- 时间复杂度:
O(n * p * 10),其中n是字符串的长度,p是给定的正整数,10 是?可能替换的数字个数。 - 空间复杂度:
O(n * p),用于存储dp数组。
空间优化
我们发现对于第层的dp[i][?]数值,只会由dp[i-1][?]决定,所以我们dp过程中可以采用滚动数组去优化空间复杂度,将其转化为O(2 * p)的空间复杂度
参考代码
int solution(string s, int p) {
int n = s.size();
vector<ll> dp(p, 0);
dp[0] = 1;
for(char c : s) {
vector<ll> new_dp(p, 0);
for(int i = 0; i < p; ++i) {
if(c == '?') {
for(int digit = 0; digit < 10; ++digit) {
new_dp[(i * 10 + digit) % p] = (new_dp[(i * 10 + digit) % p] + dp[i]) % mod;
}
} else {
int digit = c - '0';
new_dp[(i * 10 + digit) % p] = (new_dp[(i * 10 + digit) % p] + dp[i]) % mod;
}
}
dp = new_dp;
}
return dp[0];
}