问题回顾
目标: 从包含数字和问号(?)的字符串 s 中,生成可替换为正整数的形式,并找出字典序中第 k 小的正整数。若不存在这样的第 k 小正整数,则返回 -1。
重点:
- 字符串中的
?要替换为数字(0-9)。 - 替换后的结果需为正整数,不能有前导零。
- 按字典顺序顺序查找第 k 小的结果。
代码思路
1. 初始化
在代码的最开始部分,我们定义了一个空字符串 t,用于存储在处理过程中需要的临时答案。接下来的 for 循环用于遍历字符串 s:
string t = "";
for (char ch : s) {
if (ch == '?') t += '0'; // 将?预先替换为0
}
- 遍历
s,若当前字符是?,则在t中相应位置填入0。
2. 处理首位的 ?
接着,我们需要特殊处理字符串的第一位:
if (s[0] == '?') t[0] = '1'; // 如果首位为?,那么其必须至少为1
- 如果字符串的第一位是
?,它必须至少为1,以避免生成的数字出现前导零。
3. 调整 k 值
之后我们将 k 减 1,因为数组索引通常是从 0 开始,我们需要在处理时将 k 转换为基于零的索引:
k--; // 调整 k,从0开始
4. 判断是否可以生成有效数字
在生成第 k 小数字之前,我们需要确保 k 是有效的。如果 k 超出了可能生成的数字范围,则直接返回 -1:
string sk = to_string(k);
if (sk.size() > t.size() || (sk.size() == t.size() && t[0] - '0' + sk[0] - '0' > 9)) {
return "-1"; // k 超出可产生数字的范围
}
sk.size() > t.size()检查是否 k 的位数超出了字符串t,因为这意味着没有足够的数字来表示 k。- 第二个条件用于确保即使在位数相同的情况下,k 也不能导致生成的数字有前导零。
5. 计算第 k 小的数字
在确定 k 是有效的后,接下来我们需要从字符串 t 中生成第 k 小的数字。我们从 sk(将 k 转换为字符串后)开始,进行逐位加法:
int n = t.size();
int m = sk.size();
// 从后往前进行数字填充
for (int i = m - 1, j = n - 1; i >= 0; --i, --j) {
t[j] += sk[i] - '0'; // 将 k 的每一位加到 t 中
}
- 将字符串
sk从后往前一位一位加到t中。通过这个步骤,我们确保了形成的数字是字典序中第 k 小的那一个。
6. 输出生成的结果
最后,我们需要根据 s 中的 ? 替换相应的数字,并进行最终的校验:
int now = 0;
string ans;
for (char ch : s) {
if (ch == '?') {
ans += t[now++]; // 用 t 中的数字替换 ?
} else {
ans += ch; // 直接加上原字符
}
}
if (ans.size() > 1 && ans[0] == '0') return "-1"; // 检查是否有前导零
return ans;
- 在这个循环中,对于每个字符,如果是
?,则替换为字符串t中相应的数字;如果是原字符,直接加到结果中。 - 最后再检查一下如果结果字符串的长度超过 1 且其首位是
0,则返回-1防止前导零。
总结
这段代码的整体思路是通过对 ? 的合理替换来构造出需要的数字,从而找到字典序中第 k 小的正整数。代码的核心在于精确处理 ? 的替换以及控制前导零,同时通过对 k 的巧妙利用来避免生成其他不必要的数字,保证了算法的高效性。
这种方法效率高,适用于大小适中的输入字符串,确保在允许范围内计算出有效的正整数,同时考虑了各种边角情况,能够在保证正确性的基础上返回准确的结果。