给定任何正则表达式A,是否有办法将其转换为正则表达式B,它接受A接受的字符串的所有字符串和前缀。
例如,如果/ apple /是给定的正则表达式,是否有一种通用的方法将其转换为/ a | ap | app | appl | apple /
答案 0 :(得分:1)
如果您正在谈论formal regular expressions(即描述regular languages的正则表达式),那么这是一个将正则表达式转换为接受前缀的过程的过程。
任何正则表达式都有DFA;这是/apple/
的DFA(转换为失败状态):
要生成与此DFA接受的字符串前缀相匹配的DFA,如果它们位于导致接受原始DFA中的状态的路径中,则将状态转换为accepting states:
有several methods用于从DFA中读取正则表达式。如果我们使用状态删除技术,我们会得到以下DFA:
这对应于正则表达式/a|ap|app|appl|apple|/
,加上空字符串(因为空字符串是任何正则表达式的前缀)。
apple
示例很简单,但同样的技术可用于更复杂的正则表达式。例如,请考虑/(00)*1(00|1)*/
:
此DFA接受字符串00100
但不接受0010101
。在将适当的状态转换为最终状态并组合两个相同的状态后,我们有
这相当于
我们可以从中读取正则表达式/(00)*(0?|1(1|00)*0?)/
,其中包含空字符串。
此正则表达式拒绝00101
,因为它会导致原始DFA转换为失败状态,但接受“0”和“00”,因为这些字符串不会导致原始DFA进入失败状态。
答案 1 :(得分:0)
显然可以通过转换为有限状态机,将所有状态更改为接受状态,然后转换回来,如@JoshRosen 的回答所示。
这是一个更直接的算法。
为简单起见,我将假设正式定义
正则表达式 here。因此表达式是从空字符串、文字字符和运算符连接、交替 ('|') 和 Kleene 星形 ('*') 递归构建的。我还将包括运算符“?”,这样 A?
是 (|A)
的缩写。
我们想定义一个转换 P,它接受任何正则表达式 A 并输出一个正则表达式 B=P(A),它完全接受 A 接受的前缀。 以下简单的 P 递归定义就可以做到:
P(empty string) = empty string
P(c) = c? if c is a literal character
P(A?) = P(A)
P(A*) = (A* P(A))
P(A B) = (P(A) | (A P(B)))
P(A|B) = (P(A) | P(B))
正确性的证明当然是通过对正则表达式结构的归纳:观察它对于空字符串和文字字符是正确的,
并且,如果 A
和 B
是正确的,那么 A B
、A|B
、A*
和 A?
也是正确的。>
我们还可以添加以下规则,这不是绝对必要的 但在文字字符串的常见情况下(即几个文字字符的串联)会导致更有效的输出表达式:
P(a B) = (a P(B))? when a is a literal character
考虑正则表达式 /abc/
。通常我们不指定或不关心连接运算符是左关联还是右关联,因为结果接受的语言是相同的。但让我们通过两种方式解决它。
如果我们将串联视为从左到右的关联:
P(/abc/) = P((a b) c)
= P(a b) | ((a b) P(c))
= P(a b) | ((a b) c?)
= (a P(b))? | ((a b) c?)
= (a b?)? | ((a b) c?)
= /(ab?)?|abc?/
或者,如果我们将串联视为从右到左关联,我们只需重复应用 P(a B) = (a P(B))?
规则即可获得更有效的(线性大小,doesn't repeat itself)输出:
P(/abc/) = P(a (b c))
= (a P(b c))?
= (a (b P(c))?)?
= (a (b c?)?)?
= /(a(bc?)?)?/
答案 2 :(得分:-2)
取决于广义方式的含义。
\b(a(p?(p?(l?(e?)))))\b
编辑:添加背后的正面看法代表了更好的解决方案,但它完全取决于正则表达式机器的实现。