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

114 阅读4分钟

题目概述

我们需要替换字符串中的问号(?)为数字(0到9),使得替换后的字符串形成一个正整数,该整数是给定正整数 ( p ) 的倍数。由于问号的组合可能非常庞大,我们需要一种有效的方法来计算所有可能的组合。

动态规划(DP)分析

我们使用动态规划的思想,通过一个 DP 数组来跟踪可以形成不同余数的字符串数量。接下来我们详细讨论 DP 的状态定义、转移方程和实现步骤。

1. DP 状态定义

  • DP数组:定义一个 DP 数组 dp,其中 dp[j] 表示当前处理到某个字符时,形成余数为 ( j ) 的字符串数量。
  • 余数范围:余数的范围是从 0 到 ( p-1 ),因为我们关心的是数对 ( p ) 的取模效果。

2. 初始化

  • 初始状态:初始化时,dp[0] 设为 1,意味着在还未处理任何字符时,余数为 0 是唯一的状态(即空串)。

3. 状态转移

我们逐字符处理字符串,更新 DP 数组。

  • 遍历字符串:对字符串中的每个字符 ( s[i] ) 进行遍历。
  • 字符处理
    • 如果字符是数字:直接将其转化为对应的整数 ( v ) (例如,字符 '3' 转化为数字 3)。
      • 对每一个可能的余数 ( j )(0到( p-1 )),我们更新: [ ndp[(j \times 10 + v) % p] += dp[j] ]
      • 这里的意思是,现有的余数 ( j ) 通过将数字 ( v ) 添加到其后面形成的新数字,这样形成的新数字的余数为 ( (j \times 10 + v) % p )。
    • 如果字符是问号 ?:我们可以将其替换为 0 到 9 中的任意数字。
      • 在这种情况下,我们需要对每一个可能的数字(从 0 到 9)进行循环:
        for (int v = 0; v <= 9; v++) {
            for (int j = 0; j < p; j++) {
                ndp[(j * 10 + v) % p] += dp[j];
            }
        }
        
      • 对于每个数字 ( v ),同样更新 DP 状态,使得所有可能的余数都能被考虑到。

4. 更新状态

  • 每次处理完一个字符后,用新计算出的状态 ndp 更新 DP:
    dp = move(ndp);
    

5. 特殊处理:去掉前导零的情况

虽然可能形成的数字包括以零开始的情况,但作为正整数,我们需要确保最终结果不以零开头,所以在统计最终的结果时,要排除这种情况:

  • 我们只需在统计最终形成的结果时,计算不以0开头的情况。如果字符串的第一个字符是?,则它不能替换为0,但在DP过程中会自动考虑所有可能性。

6. 最终结果

  • 当处理完成所有字符后,DP 数组中 dp[0] 即为形成 ( p ) 倍数的正整数数量。这是因为余数为 0 代表的是整数是 ( p ) 的倍数。

代码实现

#include <iostream>
#include <vector>
#include <string>
using namespace std;

using ll = long long;
const int mod = 1e9 + 7;

int solution(string s, int p) {
    int n = s.size();
    vector<ll> dp(p, 0);
    dp[0] = 1; // 初始化,当没有处理任何字符时,余数为0的计数为1

    for (int i = 0; i < n; i++) {
        vector<ll> ndp(p, 0); // 建立一个新的DP状态数组
        
        if (s[i] != '?') {
            int v = s[i] - '0'; // 当前字符是数字,转为整数
            for (int j = 0; j < p; j++) {
                ndp[(j * 10 + v) % p] += dp[j]; // 更新状态
                ndp[(j * 10 + v) % p] %= mod; // 模运算
            }
        } else {
            // 当前字符是 '?'
            for (int v = 0; v <= 9; v++) { // 尝试替换成 0 到 9
                for (int j = 0; j < p; j++) {
                    ndp[(j * 10 + v) % p] += dp[j]; // 更新状态
                    ndp[(j * 10 + v) % p] %= mod; // 模运算
                }
            }
        }
        dp = move(ndp); // 更新DP状态
    }

    return dp[0]; // 最终返回余数为0的结果数量
}

int main() {
    cout << (solution("??", 1) == 100) << endl;  // 测试用例,输出100
    cout << (solution("????1", 12) == 0) << endl; // 测试用例,输出0
    cout << (solution("1??2", 3) == 34) << endl;  // 测试用例,输出34
    return 0;
}

总结

通过以上详细的 DP 的分析和实现,我们可以有效地解决在字符串中替换问号的问题,确保替换后的整数为给定正整数 ( p ) 的倍数。这种方法具有较低的时间与空间复杂度,适合处理规模较大的输入。希望这个解答对您理解 DP 方法有帮助!如果还有其他问题或需要进一步的讲解,请随时问我。