提问人:Aman 提问时间:9/8/2022 最后编辑:trincotAman 更新时间:9/12/2022 访问量:134
查找有效坑洼字符串的数量
find number of valid potholes string
问:
我收到了这个代码挑战:
你住在一个有很多坑洼的社区。给定一个由以下部分组成的字符串:
str
- “P”——表示坑洼。
- “0” - 表示这里没有坑洼,也没有 (我-1) st 和 (i+1)st 位置。
- “1” - 表示这里没有坑洼,1 个坑洼 (i-1)st 和 (i+1)st 位置。
- “2” - 表示这里没有坑洼,2 (I-1)st 和 (i+1)st 位置的坑洼。
- '?' - 表示我们不知道那里有什么。它可以是 '0'、'1'、 “2”或“P”。
报告可以创建的字符串数。如果给定的输入具有冲突的信息,则报告 0。
使用模数来报告答案。
输入在第一行具有测试数 T。 接下来的每一行都有一个输入字符串。
约束
1 <= T <= 100 1 <= str <= 1e5
例
输入
2 ?01??? PP12
输出
4 0
我在一次在线测试中得到了这个问题,我觉得我几乎达到了解决方案,但即使花了很多时间,我也无法实现它。对此的算法是什么?
答:
这可以通过从左到右扫过字符并将“0”、“1”和“2”的效果应用于它们后面的问号来解决。同时检测不一致之处(例如“0”后跟“P”,或“2”后跟“0”等)。
其效果可能是问号必须解析为“P”,也可以是没有坑洼。为了对“缺席”进行编码,我们应该使用字符“0”、“1”或“2”之一,当整个字符串被解析时,要使用的字符串直接取决于该位置具有的“P”邻居的数量。在下面的算法中,我选择使用一个新字符“_”来表示“没有坑洼”,因为我知道实际上它要么是“0”、“1”或“2”(没有自由选择)。但这并不重要,因为我们没有被要求返回有效的字符串,只要求返回它们的计数。
然后这个想法是在相反的方向上执行相同的过程 - 从右到左扫描。 在第二次扫描中,计算未解决的问号的数量:这些问号显然可以是两者(“P”或“_”)。但是,对于交替的“1”和“?”链,需要特别小心。例如,如果输入是“?1?1?1?”,我们可以将第一个“?”设为“P”,但这将决定所有其他“?”?(它们不再免费)。因此,此输入只有 2 个解决方案,具体取决于如何将其中一个“?”解析为“P”或“_”。简而言之,后跟“1”的问号不应算作“自由”选择。
其余的自由选择各代表一个二元选择,因此可能性的总数是 2 提高到自由“?”的幂。
以下是上述思想在纯 JavaScript 中的实现:
function countSolutions(s) {
// Helper function to resolve a "?" to either "P" or "_"
function set(list, i, ch) {
if (list[i] == "?") list[i] = ch; // Resolve "?"
return (list[i] == "P") == (ch == "P"); // Is false when there is a contradiction
}
// Main algorithm for a single sweep from left to right
function sweep(list) {
let count = 0;
for (let i = 2; i < list.length; i++) {
switch (list[i-1]) {
case "0":
if (!set(list, i, "_")) return 0; // Contradiction
break;
case "2":
if (!set(list, i, "P")) return 0; // Contradiction
break;
case "1":
switch (list[i-2]) {
case "?":
count--; // Undo. Take into account the chain-effect
break;
case "P":
if (!set(list, i, "_")) return 0; // Contradiction
break;
default: // "0", "1", or "_"
if (!set(list, i, "P")) return 0; // Contradiction
break;
}
break;
case "?":
count++;
}
}
return 1 << count;
}
// Convert character string to array of characters (so it is mutable)
// To ease the process, add two extra characters (denoting no pitholes)
let list = Array.from("_" + s + "_");
// Perform two sweeps in opposite directions. If the first returns 0
// then don't bother with the second sweep as there is no solution.
return sweep(list) && sweep(list.reverse());
}
// Execute the algorithm for the two test cases given in the question
const tests = [
"?01???",
"PP12"
]
for (const test of tests) {
// Output both the test and the result for it
console.log(test, countSolutions(test));
}
笔记:
- 您的问题提到了“模数”,但没有提供使用哪个模数。我想你自己应用它不会有困难。
- 代码质询指定输入/输出格式。同样,我想您首先将测试读取到变量中,然后对这些变量应用算法并生成输出不会有任何困难。
评论
0
P
您应该记住的一般技术是:
答案是最终进入接受状态的方法的总和。
这种技术适用于许多“多少个字符串”的问题。
在这种情况下,DFA 非常简单,只需要 4 个状态。让我们称它们为:
- S - 字符串的开头。尚未处理任何字符
- F - 由于前面的 1 或 2 而被迫出现下一个坑洼
- N - 接下来不能出现坑洼,但所有其他字符都有效
- H - 在坑洼之后 -- 除了 0 之外的任何字符都可以是下一个
过渡是:
S: 0 -> N, 1 -> F, P -> H
F: P -> H
N: 0 -> N, 1 -> F
H: 1 -> N, 2 -> F, P -> H
假设元组 (s,f,n,h) 给出了您可以在任何位置进入每种状态的多少种方式。在字符串的开头,您必须位于 S 中,并且您有 (1,0,0,0)
要获得下一个位置的计数,您必须处理下一个字符。从 (s,f,n,h) 开始,每个字符产生以下计数:
0 -> (0, 0, s+n, 0)
1 -> (0, s+n, h, 0)
2 -> (0, h, 0, 0)
P -> (0, 0, 0, s+f+h)
? -> (0, s+n+h, s+n+h, s+f+h) // sum of the above
DFA 中的 S、N、H 状态是接受的,因此在对每个字符执行此转换后,只需将元组中这些状态的计数相加即可得到答案。
下一个:从数组列表中可视化树
评论
?01???
001P00