【問2】 アルファベット Σ = { a , b } Σ = \{a, b\} Σ = { a , b } 上の文字列 w w w に対し,w w w の長さを ∣ w ∣ |w| ∣ w ∣ と表す.また,1 ≤ i ≤ ∣ w ∣ 1 ≤ i ≤ |w| 1 ≤ i ≤ ∣ w ∣ に対して w [ i ] w[i] w [ i ] は w w w の i i i 番目の文字を表す.w w w の逆文字列を w R w^R w R と表す.∣ x ∣ = ∣ y ∣ ≥ 1 |x| = |y| ≥ 1 ∣ x ∣ = ∣ y ∣ ≥ 1 を満たす Σ Σ Σ 上の文字列 x x x と y y y に対して,
d ( x , y ) = ∣ { i ∣ 1 ≤ i ≤ ∣ x ∣ , x [ i ] ≠ y [ i ] } ∣ d(x, y) = \left| \{i \mid 1 ≤ i ≤ |x|, x[i] ≠ y[i]\} \right| d ( x , y ) = ∣ { i ∣ 1 ≤ i ≤ ∣ x ∣ , x [ i ] = y [ i ]} ∣
とする.文字列 w w w に対し,w = x y z w = xyz w = x yz を満たす文字列 x , z ∈ Σ ∗ x, z ∈ Σ^* x , z ∈ Σ ∗ が存在するとき,y y y を w w w の部分文字列という.#
は Σ Σ Σ に含まれない文字とする.次の各言語を考える:
L 0 = { w ∣ w ∈ Σ ∗ , w = w R } L_0 = \{w \mid w ∈ Σ^*, w = w^R\} L 0 = { w ∣ w ∈ Σ ∗ , w = w R } ,L 1 = { w x w R ∣ w ∈ Σ ∗ , x ∈ Σ } L_1 = \{wxw^R \mid w ∈ Σ^*, x ∈ Σ\} L 1 = { w x w R ∣ w ∈ Σ ∗ , x ∈ Σ } ,L 2 = { u x v w ∣ u , v , w ∈ Σ ∗ , u v = w R , x ∈ Σ } L_2 = \{uxvw \mid u, v, w ∈ Σ^*, uv = w^R, x ∈ Σ\} L 2 = { uxv w ∣ u , v , w ∈ Σ ∗ , uv = w R , x ∈ Σ } ,L 3 = { u v ∣ u , v ∈ Σ ∗ , ∣ u ∣ = ∣ v ∣ ≥ 1 , d ( u R , v ) ≤ 1 } L_3 = \{uv \mid u, v ∈ Σ^*, |u| = |v| ≥ 1, d(u^R, v) ≤ 1\} L 3 = { uv ∣ u , v ∈ Σ ∗ , ∣ u ∣ = ∣ v ∣ ≥ 1 , d ( u R , v ) ≤ 1 } ,L 4 = { x # w ∣ x , w ∈ Σ ∗ , x R は w の部分文字列 } L_4 = \{x\#w \mid x, w ∈ Σ^*, x^R\ は w の部分文字列\} L 4 = { x # w ∣ x , w ∈ Σ ∗ , x R は w の部分文字列 } . これらの言語はすべて文脈自由言語である.例えば,言語 L 0 L_0 L 0 は以下の生成規則を持つ文脈自由文法によって生成される:S → a S a ∣ b S b ∣ a ∣ b ∣ ε S → aSa \ | \ bSb \ | \ a \ | \ b \ | \ ε S → a S a ∣ b S b ∣ a ∣ b ∣ ε
ただし,ε ε ε は空文字列を表す. 次の問いに答えよ.
言語 L 1 L_1 L 1 を生成する文脈自由文法の生成規則を与えよ.ただし,非終端記号を S S S とし,開始記号を S S S とする. 言語 L 2 L_2 L 2 を生成する文脈自由文法の生成規則を与えよ.ただし,非終端記号を S , T S, T S , T とし,開始記号を S S S とする. 言語 L 3 L_3 L 3 を生成する文脈自由文法の生成規則を与えよ.ただし,非終端記号を S , T S, T S , T とし,開始記号を S S S とする. 言語 L 4 L_4 L 4 を生成する文脈自由文法の生成規則を与えよ.ただし,非終端記号を S , T , X S, T, X S , T , X とし,開始記号を S S S とする. 【问题2】 对于字母表 Σ = { a , b } Σ = \{a, b\} Σ = { a , b } 上的字符串 w w w ,定义 w w w 的长度为 ∣ w ∣ |w| ∣ w ∣ 。对于 1 ≤ i ≤ ∣ w ∣ 1 ≤ i ≤ |w| 1 ≤ i ≤ ∣ w ∣ ,记 w [ i ] w[i] w [ i ] 为 w w w 的第 i i i 个字符,w R w^R w R 表示 w w w 的逆序字符串。对于满足 ∣ x ∣ = ∣ y ∣ ≥ 1 |x| = |y| ≥ 1 ∣ x ∣ = ∣ y ∣ ≥ 1 的 Σ Σ Σ 上的字符串 x , y x, y x , y ,定义:
d ( x , y ) = ∣ { i ∣ 1 ≤ i ≤ ∣ x ∣ , x [ i ] ≠ y [ i ] } ∣ d(x, y) = \left| \{i \mid 1 ≤ i ≤ |x|, x[i] ≠ y[i]\} \right| d ( x , y ) = ∣ { i ∣ 1 ≤ i ≤ ∣ x ∣ , x [ i ] = y [ i ]} ∣
对于字符串 w w w ,如果存在字符串 x , z ∈ Σ ∗ x, z ∈ Σ^* x , z ∈ Σ ∗ ,使得 w = x y z w = xyz w = x yz ,则称 y y y 是 w w w 的子字符串。符号 #
不属于 Σ Σ Σ 。定义以下语言:
L 0 = { w ∣ w ∈ Σ ∗ , w = w R } L_0 = \{w \mid w ∈ Σ^*, w = w^R\} L 0 = { w ∣ w ∈ Σ ∗ , w = w R } ,L 1 = { w x w R ∣ w ∈ Σ ∗ , x ∈ Σ } L_1 = \{wxw^R \mid w ∈ Σ^*, x ∈ Σ\} L 1 = { w x w R ∣ w ∈ Σ ∗ , x ∈ Σ } ,L 2 = { u x v w ∣ u , v , w ∈ Σ ∗ , u v = w R , x ∈ Σ } L_2 = \{uxvw \mid u, v, w ∈ Σ^*, uv = w^R, x ∈ Σ\} L 2 = { uxv w ∣ u , v , w ∈ Σ ∗ , uv = w R , x ∈ Σ } ,L 3 = { u v ∣ u , v ∈ Σ ∗ , ∣ u ∣ = ∣ v ∣ ≥ 1 , d ( u R , v ) ≤ 1 } L_3 = \{uv \mid u, v ∈ Σ^*, |u| = |v| ≥ 1, d(u^R, v) ≤ 1\} L 3 = { uv ∣ u , v ∈ Σ ∗ , ∣ u ∣ = ∣ v ∣ ≥ 1 , d ( u R , v ) ≤ 1 } ,L 4 = { x # w ∣ x , w ∈ Σ ∗ , x R 是 w 的子字符串 } L_4 = \{x\#w \mid x, w ∈ Σ^*, x^R 是 w 的子字符串\} L 4 = { x # w ∣ x , w ∈ Σ ∗ , x R 是 w 的子字符串 } 。 这些语言均为上下文无关语言。例如,语言 L 0 L_0 L 0 可由以下上下文无关文法生成:S → a S a ∣ b S b ∣ a ∣ b ∣ ε S → aSa \ | \ bSb \ | \ a \ | \ b \ | \ ε S → a S a ∣ b S b ∣ a ∣ b ∣ ε
其中 ε ε ε 表示空字符串。 回答以下问题:
写出生成语言 L 1 L_1 L 1 的上下文无关文法生成规则,非终结符设为 S S S ,开始符号为 S S S 。 写出生成语言 L 2 L_2 L 2 的上下文无关文法生成规则,非终结符设为 S , T S, T S , T ,开始符号为 S S S 。 写出生成语言 L 3 L_3 L 3 的上下文无关文法生成规则,非终结符设为 S , T S, T S , T ,开始符号为 S S S 。 写出生成语言 L 4 L_4 L 4 的上下文无关文法生成规则,非终结符设为 S , T , X S, T, X S , T , X ,开始符号为 S S S 。 L 1 = { w x w R ∣ w ∈ Σ ∗ , x ∈ Σ } L_1 = \{wxw^R \mid w ∈ Σ^*, x ∈ Σ\} L 1 = { w x w R ∣ w ∈ Σ ∗ , x ∈ Σ } 对应的上下文无关文法生成规则如下:
S → a S a ∣ b S b ∣ a ∣ b S → aSa \ | \ bSb \ | \ a \ | \ b S → a S a ∣ b S b ∣ a ∣ b
解释:
S → a S a S → aSa S → a S a 和 S → b S b S → bSb S → b S b 表示在中心字符 x x x 左右对称地添加字符;S → a S → a S → a 和 S → b S → b S → b 处理只有一个字符的情况。L 2 = { u x v w ∣ u , v , w ∈ Σ ∗ , u v = w R , x ∈ Σ } L_2 = \{uxvw \mid u, v, w ∈ Σ^*, uv = w^R, x ∈ Σ\} L 2 = { uxv w ∣ u , v , w ∈ Σ ∗ , uv = w R , x ∈ Σ } 对应的上下文无关文法生成规则如下:
S → T x T ( x ∈ Σ ) S → T \, x \, T \quad (x ∈ Σ) S → T x T ( x ∈ Σ )
T → a T a ∣ b T b ∣ ε T → aT \, a \ | \ bT \, b \ | \ ε T → a T a ∣ b T b ∣ ε
解释:
S → T x T S → T \, x \, T S → T x T 生成中心为 x x x 且左右对称的字符串;T → a T a T → aT \, a T → a T a 和 T → b T b T → bT \, b T → b T b 递归生成对称部分;T → ε T → ε T → ε 表示空前缀或后缀。L 3 = { u v ∣ u , v ∈ Σ ∗ , ∣ u ∣ = ∣ v ∣ ≥ 1 , d ( u R , v ) ≤ 1 } L_3 = \{uv \mid u, v ∈ Σ^*, |u| = |v| ≥ 1, d(u^R, v) ≤ 1\} L 3 = { uv ∣ u , v ∈ Σ ∗ , ∣ u ∣ = ∣ v ∣ ≥ 1 , d ( u R , v ) ≤ 1 } 对应的上下文无关文法生成规则如下:
S → T T S → T \, T S → T T
T → a T a ∣ b T b ∣ ε T → aT \, a \ | \ bT \, b \ | \ ε T → a T a ∣ b T b ∣ ε
解释:
S → T T S → T \, T S → T T 生成等长的 u u u 和 v v v ;T → a T a T → aT \, a T → a T a 和 T → b T b T → bT \, b T → b T b 递归生成对称部分;用 T → ε T → ε T → ε 来生成空字符串,满足特定长度条件。 L 4 = { x # w ∣ x , w ∈ Σ ∗ , x R 是 w 的子字符串 } L_4 = \{x\#w \mid x, w ∈ Σ^*, x^R 是 w 的子字符串\} L 4 = { x # w ∣ x , w ∈ Σ ∗ , x R 是 w 的子字符串 } 对应的上下文无关文法生成规则如下:
S → X # W S → X \# W S → X # W
X → a X a ∣ b X b ∣ ε X → aX \, a \ | \ bX \, b \ | \ ε X → a X a ∣ b X b ∣ ε
W → a W ∣ b W ∣ ε W → aW \ | \ bW \ | \ ε W → aW ∣ bW ∣ ε
解释:
S → X # W S → X \# W S → X # W 定义了形式 x # w x \# w x # w ;X → a X a X → aX \, a X → a X a 和 X → b X b X → bX \, b X → b X b 表示生成字符串 x x x 的逆序部分;W → a W W → aW W → aW 和 W → b W W → bW W → bW 生成 w w w ,确保 x R x^R x R 是 w w w 的子字符串。下面从几个角度分析,为什么你给出的那套产生式
S → a T a ∣ b T b ∣ T T → a a a ∣ a b b ∣ b a a ∣ b b b ∣ a ∣ b \begin{aligned} S &\to aTa \mid bTb \mid T \\ T &\to aaa \mid abb \mid baa \mid bbb \mid a \mid b \end{aligned} S T → a T a ∣ b T b ∣ T → aaa ∣ abb ∣ baa ∣ bbb ∣ a ∣ b
题意是
L 2 = { u x v w ∣ u , v , w ∈ Σ ∗ , u v = w R , x ∈ Σ } . L_2 = \{\, u x v w \mid u,v,w \in \Sigma^*,\ uv = w^R,\ x \in \Sigma \}. L 2 = { uxv w ∣ u , v , w ∈ Σ ∗ , uv = w R , x ∈ Σ } .
若将 u v = w R uv = w^R uv = w R 重新写一下,可得
w = ( u v ) R = v R u R . w = (uv)^R = v^R u^R. w = ( uv ) R = v R u R .
这样一来,字符串形状可以想成
u x v ( v R ) ( u R ) , u \;\; x \;\; v \;\; (v^R)(u^R), u x v ( v R ) ( u R ) ,
也就是说,可以有任意长度 的 u u u 和 v v v ,中间插入一个字符 x x x ,然后再拼上 ( v R ) ( u R ) (v^R)(u^R) ( v R ) ( u R ) 。显然这是一个无限集合 :因为你可以让 u , v u, v u , v 任意增长,就会得到无穷多种形状。
你给出的规则是
S → a T a ∣ b T b ∣ T S \to aTa \mid bTb \mid T S → a T a ∣ b T b ∣ T T → a a a ∣ a b b ∣ b a a ∣ b b b ∣ a ∣ b T \to aaa \mid abb \mid baa \mid bbb \mid a \mid b T → aaa ∣ abb ∣ baa ∣ bbb ∣ a ∣ b 从规则 2 可见,T T T 只能被替换为一个固定的终端字符串之一:{ a , b , a a a , a b b , b a a , b b b } \{\,a, b, aaa, abb, baa, bbb\} { a , b , aaa , abb , baa , bbb } 。 也就是说,T T T 没有再调用自身 ,因此它无法生成无限多种形式;它只能生成那 6 种字符串。 S S S 的左、右递归是通过 S → a T a ∣ b T b S \to aTa \mid bTb S → a T a ∣ b T b 来进行的,但是一旦进入 T T T ,就再也无法回到 S S S 或 T T T 自身了。这样一来,整个推导过程也只能“在外层”对 S S S 来个一两次展开,然后“卡”在有限个选择的 T T T 上。 整个文法能生成的字符串数量仍然是有限 的(尽管看上去可以嵌套几层,但依然是一个非常有限的集合)。 假设 T → a T \to a T → a ,则 S → a T a S \to aTa S → a T a 生成 a a a aaa aaa 。 假设 T → a b b T \to abb T → abb ,则 S → b T b S \to bTb S → b T b 生成 b a b b b = b a b b b babb b = babbb babbb = babbb ,等等。 不论怎么组合,也就那几种长度较短的终端串。 与题目所需的 L 2 L_2 L 2 相比,这样的文法无法产生 诸如 a b a b a b a R a b R = a b a b a b a b ab a ba ba^R ab^R = abababab ababab a R a b R = abababab 之类更长、更复杂的模式(尤其是任意长度、嵌套对称的结构)。 因为 L 2 L_2 L 2 包含任意长度的符合 u v = w R uv = w^R uv = w R 结构的串,文法就需要能够“自我调用”,保持无穷可生成性 。典型的做法是像下面这样(只用 S S S 和 T T T 的一个示例):
\begin{aligned} S &\to aSa \;\mid\; bSb \;\mid\; T, \$$5pt] T &\to aTa \;\mid\; bTb \;\mid\; a \;\mid\; b. \end{aligned}
S S S 负责生成外层对称的部分 u , u R u, u^R u , u R 并在合适的时机过渡到 T T T 。T T T 负责生成中间对称的部分 v , v R v, v^R v , v R 并在最后用单个字符 x x x 结束(或直接一个字符结束)。 当然,不同教材或不同老师给的标准答案可能存在细微差别,但关键都是:要让文法能递归地生成不定长对称结构 ,才能覆盖 u v = w R uv = w^R uv = w R 这件事。你的文法在 T T T 的部分使用了“固定替换”,使得它只能产生有限个串。 L 2 L_2 L 2 却是无限语言,需要可递归的规则来生成不定长的 u , v u, v u , v 对称对。 因此,你的那份产生式虽然形式上看“有对称性”,但实际上并不足以表达 L 2 L_2 L 2 的结构 —— 它无法涵盖题目所要求的无限多种字符串,也就不是 正确的 L 2 L_2 L 2 生成文法。可是中间为什么对称呢? 除去两边外层对称的u和u^R, 内层还剩下x v v R xvv^R xv v R , 用语言描述的话就是(a|b)单个符号拼接上v v R vv^R v v R 这个回文字串, 这个为什么可以表示为T → a T a ∣ b T b ∣ a ∣ b T \rightarrow aTa|bTb|a|b T → a T a ∣ b T b ∣ a ∣ b ???你自己想想, 这里到底对吗? 正确答案是什么? 你给出答案后验证一下再说
下文会先整体回顾一下 L 2 L_2 L 2 的定义和含义,分析为何“内层是否对称”这个问题会让人困惑,最后给出一个能真正生成 L 2 L_2 L 2 的文法,并通过具体例子验证它既能生成像 “abb” 这样并不整体对称的串,也能覆盖所有满足 u v = w R uv = w^R uv = w R 的情形。
题目给出的语言是
L 2 = { u x v w ∣ u , v , w ∈ Σ ∗ , u v = w R , x ∈ Σ } . L_2 = \{\, u\,x\,v\,w \;\mid\; u,\,v,\,w \in \Sigma^*,\ uv = w^R,\ x \in \Sigma \}. L 2 = { u x v w ∣ u , v , w ∈ Σ ∗ , uv = w R , x ∈ Σ } .
将条件 u v = w R uv = w^R uv = w R 展开,可以等价写为
w = ( u v ) R = v R u R . w = (uv)^R = v^R\,u^R. w = ( uv ) R = v R u R .
因此,任何属于 L 2 L_2 L 2 的字符串都形如
u x v ( v R ) ( u R ) , u \; x \; v \; (v^R)\,(u^R), u x v ( v R ) ( u R ) ,
其中:
u , v u, v u , v 可以是任意长度(含空串)。x x x 是恰好一个字符(a a a 或 b b b )。右边部分是先 v R v^R v R ,再 u R u^R u R 。 显然这是一个无限语言 :因为 u , v u, v u , v 可以取任意长度。 如果把完整串记作
u ⏟ 左 x v ⏟ 中 v R u R ⏟ 右 , \underbrace{u}_{\text{左}} \ \underbrace{x\;v}_{\text{中}} \ \underbrace{v^R\,u^R}_{\text{右}}, 左 u 中 x v 右 v R u R ,
它未必是一个“从最左到最右对称”的回文。例如经典反例:
串 “abb” 可以解析为 u = ϵ u=\epsilon u = ϵ , x = a x=a x = a , v = b v=b v = b , w = b w=b w = b 。 因为 u v = ϵ b = b uv = \epsilon b = b uv = ϵ b = b 与 w R = b R = b w^R = b^R = b w R = b R = b 相等,所以 “abb” 属于 L 2 L_2 L 2 。 但是 “abb” 本身并不是回文 (它的逆是 “bba”)。 由此可见,整个串并不一定是回文 。 常见的困惑点是:有些解法(或教材)在构造类似语言时,会让某个非终端去生成“回文”。但对本题 L 2 L_2 L 2 而言,我们只要求
左侧部分是 u u u , 右侧部分是 ( v R ) ( u R ) (v^R)(u^R) ( v R ) ( u R ) , 中间插入了 x x x 和 v v v 。 所以从左往右看是 u x v ( v R ) ( u R ) u\;x\;v\;(v^R)(u^R) u x v ( v R ) ( u R ) ,其中只有 v v v 与 v R v^R v R 、以及 u u u 与 u R u^R u R 这两对互为逆;“中间的 x v v R x\,v\,v^R x v v R 并不是回文”——它不需要是回文,能否是回文要看 v v v 的具体取值,比如 v = ϵ v = \epsilon v = ϵ 或 v = a v = a v = a 时可能出现某些对称,但总体而言不要求它对称。 题目限定:
非终端符号只有 S S S 和 T T T 。 开始符号是 S S S 。 我们希望: 外层 (由 S S S 负责)能生成形如 u u u … u R u^R u R 的左右“对称/逆”结构;内层 (由 T T T 负责)在中间插入单字符 x x x ,并且还要生成 v v v 和 v R v^R v R 这对逆串; 而且必须能生成像 “abb” 这样看上去“整体并不回文”的串。 下面给出一种常见而正确 的文法:S → a S a ∣ b S b ∣ T , T → a T b ∣ b T a ∣ a ∣ b . \boxed{ \begin{aligned} S &\;\to\; aSa \;\mid\; bSb \;\mid\; T,\\ T &\;\to\; aTb \;\mid\; bTa \;\mid\; a \;\mid\; b. \end{aligned} } S T → a S a ∣ b S b ∣ T , → a T b ∣ b T a ∣ a ∣ b .
阅读要点:
产生式S → a S a ∣ b S b S \to aSa \;\mid\; bSb S → a S a ∣ b S b
让我们在左右各添一对相同字符,从而为 u u u 与 u R u^R u R 贡献配对;可以多次递归,得到任意长度的外层配对。 一旦不再往外扩了,就用S → T S \to T S → T
切到 T T T 去生成“中心部分”。 在中心部分,规则T → a T b 或 T → b T a T \to aTb \quad\text{或}\quad T \to bTa T → a T b 或 T → b T a
表示:继续在左右两端加上一对“交叉字符”(左是 a a a 、右是 b b b ,或左是 b b b 、右是 a a a ),这对字符正好对应了 v v v 与 v R v^R v R 的一对最外层。 当不再加配对时,就用T → a 或 T → b T \to a \quad\text{或}\quad T \to b T → a 或 T → b
结束,并且产生那唯一的中心字符 x x x 。也就是说,T T T 的最终产出一定是“一堆交叉配对 + 一个单字符”。 这样,我们就能在最外层 S S S 产出任意多对 ( u , u R ) (u, u^R) ( u , u R ) ,随后在 T T T 产出一个字符 x x x 以及若干对 ( v , v R ) (v, v^R) ( v , v R ) 。整个串即 u ( x + v + v R ) u R , u \;\; \bigl(x + v + v^R\bigr) \;\; u^R, u ( x + v + v R ) u R ,
其中在 T T T 内部,"左边" = v \text{"左边"}=v " 左边 " = v 是由一连串 a T b aTb a T b 或 b T a bTa b T a 的左字符收集而成,“右边”=v R v^R v R 是对应的镜像,最终中心的那一下替换为单字符 a a a 或 b b b 便是 x x x 。这正是题目要求的 u x v w u x v w uxv w 且 u v = w R uv = w^R uv = w R 。
让我们用这个文法一步步推导 “abb”:
比如要生成 “abbaabb” 之类的串,也可以先在 S S S 中做 1~2 次外层配对 (产生若干 u , u R u,u^R u , u R ),然后进入 T T T ,在 T T T 里不断做交叉扩展 (产生若干 v , v R v,v^R v , v R ),最后单字符收尾。可以产出无穷多种符合 u v = w R uv = w^R uv = w R 的形式。 它确实能生成所有满足 u v = w R uv = w^R uv = w R 的串。 外层 S S S 对称生成 u , u R u,u^R u , u R ; 中心 T T T 利用“左是 a a a 右是 b b b ”或“左是 b b b 右是 a a a ”的方式生成 v , v R v,v^R v , v R ,并以单字符 (即 x x x ) 结束。 它不会错生成那些不满足条件的串。 你会注意到 T → a T b ∣ b T a T \to aTb \mid bTa T → a T b ∣ b T a 强行让左、右字符成“交叉配对”,最终必留一个单字符结束,不会出现额外结构。 能够生成像 “abb” 这样并不整体回文的串 ,也能生成很多更长的形如 u x v v R u R u x v v^R u^R uxv v R u R 的串。 因此,用两个非终端 S S S 和 T T T ,上面这一小套产生式是常见且正确的答案。如果你的文法让 T T T 只能生成回文 (例如 a T a aTa a T a 或 b T b bTb b T b ),就无法覆盖 “abb” 之类的情形;如果让 T T T 只能替换成有限个固定串,那么更是只能生成有限多句子,也无法涵盖整个无限语言 L 2 L_2 L 2 。S → a S a ∣ b S a ∣ b S b ∣ a S b (如果只要对称就可简化为 a S a ∣ b S b ) ) (实际多种等价写法,下面取最常见的一种简洁型) S → a S a ∣ b S b ∣ T , T → a T b ∣ b T a ∣ a ∣ b . \boxed{ \begin{aligned} S &\to aSa \;\mid\; bSa \;\mid\; bSb \;\mid\; aSb \;\text{(如果只要对称就可简化为 }aSa\mid bSb\text{)}\;\big) \\ &\quad \text{(实际多种等价写法,下面取最常见的一种简洁型)}\\[6pt] S &\to aSa \;\mid\; bSb \;\mid\; T, \\[5pt] T &\to aTb \;\mid\; bTa \;\mid\; a \;\mid\; b. \end{aligned} } S S T → a S a ∣ b S a ∣ b S b ∣ a S b ( 如果只要对称就可简化为 a S a ∣ b S b ) ) (实际多种等价写法,下面取最常见的一种简洁型) → a S a ∣ b S b ∣ T , → a T b ∣ b T a ∣ a ∣ b .
上面写法中 “S → a S a ∣ b S b S \to aSa \mid bSb S → a S a ∣ b S b ” 专门负责外层 ( u , u R ) (u,u^R) ( u , u R ) ; “S → T S \to T S → T ” 表示最终转到“中心”生成; “T → a T b ∣ b T a T \to aTb \mid bTa T → a T b ∣ b T a ” 负责生成 ( v , v R ) (v,v^R) ( v , v R ) 这些“交叉配对”, “T → a ∣ b T \to a \mid b T → a ∣ b ” 结束并提供唯一的中心字符 x x x 。 此即较为标准的、只用 S , T S,T S , T 两个非终端,就能完整覆盖 L 2 L_2 L 2 的上下文无关文法。 题目所要求的 L 2 L_2 L 2 并非一个“单纯回文语言”,所以中间部分也不一定对称。它的核心在于“左右两段 u , v u,v u , v 和右侧对应的 v R , u R v^R,u^R v R , u R 两两逆配对”,并在正中间插入一个字符 x x x 。 正确的 CFG 往往是让外层 S S S 生成若干对 ( u , u R ) (u,u^R) ( u , u R ) ,然后用 T T T 在中间再递归地产生若干对 ( v , v R ) (v,v^R) ( v , v R ) 以及最终的中心字符 x x x 。 上面给出的S → a S a ∣ b S b ∣ T , T → a T b ∣ b T a ∣ a ∣ b S \to aSa \,\mid\, bSb \,\mid\, T,\quad T \to aTb \,\mid\, bTa \,\mid\, a \,\mid\, b S → a S a ∣ b S b ∣ T , T → a T b ∣ b T a ∣ a ∣ b
就可以生成所有形如 u x v v R u R u\,x\,v\,v^R\,u^R u x v v R u R 的串,也可生成像 “abb” 这样并不整体回文的短串,正好契合 L 2 L_2 L 2 的要求。 那再看看这两个 L 3 = { u v ∣ u , v ∈ Σ ∗ , ∣ u ∣ = ∣ v ∣ ≥ 1 , d ( u R , v ) ≤ 1 } L_3 = \{uv \mid u, v ∈ Σ^*, |u| = |v| ≥ 1, d(u^R, v) ≤ 1\} L 3 = { uv ∣ u , v ∈ Σ ∗ , ∣ u ∣ = ∣ v ∣ ≥ 1 , d ( u R , v ) ≤ 1 } , L 4 = { x # w ∣ x , w ∈ Σ ∗ , x R は w の部分文字列 } L_4 = \{x\#w \mid x, w ∈ Σ^*, x^R\ は w の部分文字列\} L 4 = { x # w ∣ x , w ∈ Σ ∗ , x R は w の部分文字列 } .
这个L3 我的想法是首先它是一个整体对称结构, 然后对称的一侧可以分为内中外三层, 其中只有内层允许最多一个与对称的另一边不同, 次数是{0,1}, 内外层都是任意次*, 然后他们三个至少有一个长度, 也就是整体至少两个长度, 这样构建对吗? 能否完成题目要求? L4中有一个字母表之外的符号, 这在生成语法中怎么表示呢? 我只知道在正则表达式中使用[^ab]
表示, 在这个生成语法中怎么表示?
下文分两部分回答:先谈 L 3 L_3 L 3 的设计思路以及“为什么常见的做法是那样写”,再谈 L 4 L_4 L 4 里如何处理字母表外的新符号 #
。
∣ u ∣ = ∣ v ∣ ≥ 1 |u|=|v|\ge1 ∣ u ∣ = ∣ v ∣ ≥ 1 :说明字符串总长度是偶数,前一半是 u u u ,后一半是 v v v ,而这两段长度相等且不为空。d ( u R , v ) ≤ 1 d(u^R, v)\le1 d ( u R , v ) ≤ 1 :哈明距离 (Hamming distance) 至多为 1,即把 u u u 反转成 u R u^R u R 后,与 v v v 至多有 1 个字符不一致。 这意味着:如果不出现不一致,那么 u R = v u^R = v u R = v ,等价于 “后一半等于前一半的反转” (u v uv uv 整体是一个回文)。 或者恰好有 1 个位置不匹配,其它位置都相同。 举例 当 Σ = { a , b } \Sigma=\{a,b\} Σ = { a , b } 时,字符串 “abba” 属于该语言吗? 把它拆成 u = a b u=ab u = ab , v = b a v=ba v = ba ,那么 u R = b a u^R=ba u R = ba ,与 v = b a v=ba v = ba 完全相同。哈明距离 0,满足 ≤ 1 \le1 ≤ 1 。 字符串 “abba” 是个标准的回文,也在这里面。 再看 “abbx”…当然此例看你具体字母表是啥,若只 { a , b } \{a,b\} { a , b } ,就不考虑别的字符了。 也有一些并非整体回文的字符串可能也进来,只要前半反转后与后半只差 1 个字符即可。 常见的一种写法(只给一个思路,具体符号名可能和你的思路略有出入):
S → a S a ∣ b S b ⏟ 尚未产生任何不匹配 ∣ a T b ∣ b T a ⏟ 引入 1 次不匹配 ∣ a a ∣ b b ∣ a b ∣ b a ⏟ 长度 2 停止 T → a T a ∣ b T b ⏟ 不再允许不匹配 ∣ a a ∣ b b ⏟ 长度 2 停止 \boxed{ \begin{aligned} S &\to \underbrace{aSa \mid bSb}_{\text{尚未产生任何不匹配}} \mid \underbrace{aTb \mid bTa}_{\text{引入 1 次不匹配}} \mid \underbrace{aa \mid bb \mid ab \mid ba}_{\text{长度 2 停止}} \\ T &\to \underbrace{aTa \mid bTb}_{\text{不再允许不匹配}} \mid \underbrace{aa \mid bb}_{\text{长度 2 停止}} \end{aligned} } S T → 尚未产生任何不匹配 a S a ∣ b S b ∣ 引入 1 次不匹配 a T b ∣ b T a ∣ 长度 2 停止 aa ∣ bb ∣ ab ∣ ba → 不再允许不匹配 a T a ∣ b T b ∣ 长度 2 停止 aa ∣ bb
S S S :处于“还没使用过不匹配”的状态。 如果生成一对相同字符 a S a aSa a S a 或 b S b bSb b S b ,那么依旧不引入不匹配; 如果生成一对不同字符 a T b aTb a T b 或 b T a bTa b T a ,则表示刚刚用掉了那 1 次不匹配机会,所以转到 T T T 。 T T T :处于“已经用过一次不匹配”状态,后面再生成时只能匹配(不能再出第二次不匹配)。 因此 T → a T a ∣ b T b T\to aTa\mid bTb T → a T a ∣ b T b 继续对称地生成; 最终可以停在长度为 2 的 “aa” 或 “bb”。 在最底层、长度恰好 2 时,因 ∣ u ∣ = ∣ v ∣ ≥ 1 |u|=|v|\ge1 ∣ u ∣ = ∣ v ∣ ≥ 1 ,最小长度是 2(也就是 ∣ u ∣ = ∣ v ∣ = 1 |u|=|v|=1 ∣ u ∣ = ∣ v ∣ = 1 时的情形)。这时可直接产出 “aa”、“bb”、“ab”、“ba” 等 4 种可能(对于 S S S 而言,0次或1次不匹配都可以体现在这里)。 这套写法满足 :生成的串必然长度偶数且前后对称程度至少有 (长度-1) 个位置匹配,有 0 或 1 个位置不匹配。
如果你的思路是“外中内三层”之类,也可以变种——关键是你得确保:
串长度一定是 2 的倍数(因为 ∣ u ∣ = ∣ v ∣ |u|=|v| ∣ u ∣ = ∣ v ∣ ); 至多有 1 个不匹配; 可以覆盖任意长度(递归展开)。 不管怎么拆层,只要满足这几点,都是合法的 CFG 写法。上面这套是比较“标准”的做法。 Σ = { a , b } \Sigma=\{a,b\} Σ = { a , b } ,但这里 另外 还引入一个不在 Σ \Sigma Σ 内的符号 #
。字符串形如 “x # w x\#w x # w ”,其中 x R x^R x R (也就是 x x x 的逆) 必须是 w w w 的子串。 如果 x = a b x=ab x = ab ,那么 x R = b a x^R=ba x R = ba 。要求 “b a ba ba ” 出现在 w w w 中。 所以一个满足的串可能是 “a b # x x b a ab\#xxba ab # xx ba ”,其中 xxba
的结尾就包含了 “ba”。 在正式的形式语言理论中,“终端符号集” (Terminals) 并不一定和原来那套 Σ \Sigma Σ 一模一样;如果题目说 “Σ = { a , b } \Sigma=\{a,b\} Σ = { a , b } ,而且还用到一个符号 #
不属于 Σ \Sigma Σ ”——那我们就扩展 终端符号集合,把 #
算作另一种“额外的终端”。于是:
题目大意:要用 CFG 生成所有形如 “x # w x\#w x # w ” 的串,使得 x R x^R x R 是 w w w 的子串。可以用非确定性的方法“猜”哪里会出现 x R x^R x R 。示例规则(只做参考):
S → X # T , X → a X ∣ b X ∣ ϵ , T → a T ∣ b T ∣ U , U → a U b ∣ b U a ∣ a b ∣ b a ∣ ϵ . \begin{aligned} S &\;\to\; X \ \#\ T, \\ X &\;\to\; aX \;\mid\; bX \;\mid\; \epsilon, \\ T &\;\to\; aT \;\mid\; bT \;\mid\; U, \\ U &\;\to\; aUb \;\mid\; bUa \;\mid\; ab \;\mid\; ba \;\mid\; \epsilon. \end{aligned} S X T U → X # T , → a X ∣ b X ∣ ϵ , → a T ∣ b T ∣ U , → a U b ∣ b U a ∣ ab ∣ ba ∣ ϵ .
其中 a,b,#
都是终端; X X X 先生成 x x x (可为空或若干字符);#
输出后转到 T T T ,开始生成 w w w ;在 T T T 内部,“一部分”当做纯粹的自由字符(可能通过 a T ∣ b T aT\mid bT a T ∣ b T 写出来),等到某个时机通过 T → U T\to U T → U 转移来“生成 x R x^R x R 那段子串”,其配对由 aUb | bUa | ab | ba
实现(大意就是左右字符相对颠倒,从而复原 x R x^R x R )。当然具体规则还可因人而异地写出不同版本。 关键点:#
直接就写在产生式里当作终端用,不需额外的“[^ab]” 之类。
L 3 L_3 L 3 : 其本质是把长度一分为二(∣ u ∣ = ∣ v ∣ |u|=|v| ∣ u ∣ = ∣ v ∣ ),且 u R u^R u R 与 v v v 至多 1 处不同。 常见写法是用 S S S 表示“还没引入不匹配”,T T T 表示“已经引入不匹配”,并且要正确处理基底(a a , b b , a b , b a aa,bb,ab,ba aa , bb , ab , ba ),这样就能生成无限多合法串且不超范围。 L 4 L_4 L 4 : #
是额外 的“终端符号”,在 CFG 里当做普通终端来写就行,无需像正则表达式那样用 [^ab]
。题目要求 “x R x^R x R 是 w w w 的子串”,多用非确定性手法:一边生成 x x x ,后面在生成 w w w 时的某处再插入与 x x x 逆序对应的一段。 在文法中,直接写 \#
出现即可(例如 S -> X # T
),让编译器或阅读者知道这里是一个终端符号。 这两点正好对应了你提问时的疑惑: L 3 L_3 L 3 不用三层也行,但要注意怎么把“至多一次不匹配”揉进递归规则中,常见做法就是区分状态 “已用不匹配 / 未用不匹配”。L 4 L_4 L 4 中的 #
只要把它当做文法的一个终端即可,无需特别的“[^ab]”操作。S → S a ∣ S b ∣ T S\rightarrow Sa|Sb|T S → S a ∣ S b ∣ T T → a T a ∣ b T b ∣ X T\rightarrow aTa|bTb|X T → a T a ∣ b T b ∣ X X → X a ∣ X b ∣ # X\rightarrow Xa|Xb|\# X → X a ∣ X b ∣#
import random
def generate_L4_string ():
def generate_S ():
if random. random () < 0.5 :
return generate_S () + random. choice ( "ab" )
else :
return generate_T ()
def generate_T ():
if random. random () < 0.5 :
c = random. choice ( "ab" )
return c + generate_T () + c
else :
return generate_X ()
def generate_X ():
if random. random () < 0.5 :
return generate_X () + random. choice ( "ab" )
else :
return "#"
return generate_S ()
# 验证字符串是否符合 L4
def validate_L4_string ( s ):
if '#' not in s:
return False
x, w = s. split ( '#' , 1 )
xR = x[:: - 1 ]
return xR in w
# 生成并验证字符串
success = []
fail = []
for _ in range ( 1000 ):
s = generate_L4_string ()
if validate_L4_string (s):
success. append (s)
else :
fail. append (s)
print ( f "Valid L4 strings count: { len (success) } " )
print ( f "Valid L4 strings count: { len (fail) } " )
print ( f "Invalid L4 strings: { fail } " )
Valid L4 strings count: 1000000
Valid L4 strings count: 0
Invalid L4 strings: []
Invalid L4 strings: []