小U的问号替换问题 | 豆包MarsCode AI刷题

109 阅读2分钟

问题描述

小C拿到了一个由数字字符和 ? 组成的字符串,她的目标是将所有的 ? 替换成数字字符,使得替换后的字符串表示的十进制整数成为正整数 pp 的倍数。由于方案数可能非常大,需要对最终的结果取模 109+710^9+7

测试样例

样例1:

输入:s = "??",p = 1
输出:100

样例2:

输入:s = "????1",p = 12
输出:0

样例3:

输入:s = "1??2",p = 3
输出:34

问题分析

我们考虑没有?的情况去判断一个数是不是pp的倍数,我们可以直接使用高精度模拟的方法,从前往后去计算每位数字对pp取模的结果,然后乘以1010加到下一位上直到各位,然后,判断他是不是恰好对pp取模后为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 数组。

务必注意在转移过程中,需要实时对 109+710^9+7 取模,否则方案数会很快超出long long存储的上限。

结果

  • 最终结果为 dp[n][0],其中 n 是字符串的长度,表示整个字符串模 p 余 0 的方案数。

复杂度分析

  • 时间复杂度O(n * p * 10),其中 n 是字符串的长度,p 是给定的正整数,10 是 ? 可能替换的数字个数。
  • 空间复杂度O(n * p),用于存储 dp 数组。

空间优化

我们发现对于第ii层的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];
}