我必须检查是否可以从 Chomsky 范式的给定上下文中派生一个字符串。我正在使用 C++。
维基百科文章中有非常好的伪代码涵盖了 CYK 算法,但我不能很好地理解它。
有人会这么好心地帮助我,给我另一个 CYK 算法的伪代码,或者解释一下 wiki 文章中的那个吗?
CYK 算法以乔姆斯基范式的 CFG 作为输入。这意味着每个产品要么具有形式
现在,假设您有一个字符串 w,并且您想查看是否可以从起始符号为 S 的文法中导出它。有两种选择:
请注意,此处的选项 (2) 最终成为递归解析问题:要查看是否可以从 S 导出 w,请查看是否可以从 A 导出 x 和从 B 导出 y。
有了这种见解,下面是递归函数的伪代码,您可以使用它来查看非终结符 S 是否派生出字符串 w:
bool canDerive(nonterminal S, string w) {
return canDeriveRec(S, w, 0, w.size());
}
/* Can you derive the substring [start, end) of w from S? */
bool canDeriveRec(nonterminal S, string w, int start, int end) {
/* Base case: Single characters */
if (end - start == 1) {
return whether there is a production S -> a, where a = w[start];
}
/* Recursive case: Try all possible splits */
for (each production S -> AB) {
for (int mid = start + 1; mid < end; mid++) {
if (canDeriveRec(A, w, start, mid) &&
canDeriveRec(B, w, mid, end)) {
return true;
}
}
}
return false;
}
这个算法可以正常工作,但是如果你绘制出递归的形状,你会发现
事实上,不同的可能调用的数量是 O(n 2 N),其中 n 是输入字符串的长度(对于开始和结束索引的每个可能组合),N 是语法中非终结符的数量。这些观察表明,该算法将受益于记忆化或动态编程,这取决于您碰巧认为哪种方法更好。
当您采用上述递归算法并记住结果时,或者等效地当您将上述递归算法转换为动态规划问题时,您会得到 CYK 算法。
有 O(n 2 N) 个可能的递归调用。对于每个尝试的生产,它确实 O(n) 工作。如果一个非终结符平均有 P 个产生式,这意味着总运行时间是 O(n 3 NP),对于固定语法来说是 O(n 3 )。