我正在阅读龙书,并且卡在一些正则表达式练习上,很有趣,有人可以帮忙吗? 请为以下语言编写正则表达式:
问题1:
a和b的所有字符串都带有偶数a和b的奇数。
问题2:
所有不包含子序列abb的字符串。 (s的子序列是通过删除零个或多个不一定是s的连续位置而形成的任何字符串。例如,baan是香蕉的子序列。)
问题3:
没有重复数字的所有数字串(重复数字不一定是连续的)。
问题4:
所有数字串最多只有一个重复数字(重复数字不一定是连续的)。
请使用POSIX语法,以便我们能够很好地理解它。另外我想知道我们是否只能使用三种基本的语言操作,即联合,连接和闭包来实现它们?
感谢。
答案 0 :(得分:0)
如果你有一个严格的正则表达式(连接,联合,闭包),你将能够将它转换为POSIX正则表达式。如果您有POSIX正则表达式,您将能够将其转换为任何更复杂的正则表达式引擎。
假设(首先阅读)
这个答案假设JavaScript正则表达式的支持程度(具有前瞻但没有后瞻),但我们还假设.
匹配任何字符,无例外。这里的正则表达式假定整个字符串与模式匹配。
问题1
^(?=(?:b*ab*a)*b*$)(?=(?:a*ba*b)*a*ba*$)[ab]*$
可以为此问题构造DFA,因此,只有连接,联合和闭包的正则表达式存在。 (DFA中各州的数字表示a和b的数量的平价)。
然而,严格的正则表达式是非常混乱的;环顾四周简化了我们的思维方式。
(?=(?:b*ab*a)*b*$)
是一个预测,(由于^
和$
的影响)检查整个字符串是否匹配(b*ab*a)*b*
- 这意味着偶数a
,其中b
可选地且有限地在其间和周围有许多。(?=(?:a*ba*b)*a*ba*$)
是一个预测,检查整个字符串是否匹配(a*ba*b)*a*ba*
- 这意味着奇数b
,{{1}可选地,在周围和周围有许多有限的。我们需要在字符串中进行3次传递,以检查字符串是否满足上述正则表达式的给定属性。
使用POSIX正则表达式,您可以针对这2个正则表达式测试字符串2次以断言2个属性:
a
但是由于存在严格的正则表达式并且可行,因此存在单个POSIX正则表达式来匹配满足属性的字符串。有关将DFA转换为严格正则表达式的方法,请阅读this post。
问题2
^(b*ab*a)*b*$
^(a*ba*b)*a*ba*$
只是前瞻,检查无法匹配子序列^(?!.*a.*b.*b).*$
。
由于没有指定字母表,可以将其解释为任何字符,所以用严格的正则表达式写这个字符是可能的,但是不切实际。但是,如果允许否定字符类,则可以轻松编写:
abb
(以上是严格的正则表达式,但可以很容易地重写为POSIX正则表达式)
交替的3个项目(从右到左)对应于DFA中的3个状态:尚未找到([^a]*a[^b]*b[^b]*|[^a]*a[^b]*|[^a]*)
;有一个a
,但前面没有a
;之后有一个b
和a
,但是还没有找到第二个b
。 DFA中还有另一个陷阱状态(无法转换到目标状态):已找到子序列b
。
问题3
abb
使用否定预测,我们将检查是否存在匹配,我们可以找到重复的字符。由于这是一个数字字符串,我通过指定至少1位^(?!\d*(\d)\d*\1)\d+$
来禁止匹配空字符串。这个正则表达式的关键点是使用捕获的组和反向引用。
同样,对此存在严格的正则表达式,但这是不切实际的,因为编写这样的正则表达式几乎相当于逐个列出所有可能的匹配。共有\d+
个匹配项(允许前导0)。没有办法绕过它,因为是否允许下一个字符在很大程度上取决于到目前为止已处理的所有字符。
这在POSIX正则表达式中也是不切实际的,因为没有机制可以否定捕获组。
问题4
无法为这一个提出更好的正则表达式...
Sum [i = 1..10] ( 10! / i! )
失败有2个条件:数字重复次数超过2次,或者重复2个不同的数字。
^(?!(?=.*(.).*\1).*((?!\1).).*\2)(?!.*(.)(?:.*\3){2})\d+$
检查没有重复的2个不同的数字。这个前瞻(?!(?=.*(.).*\1).*((?!\1).).*\2)
选出一个正在重复的字符(至少两次),而(?=.*(.).*\1)
选择的数字与之前检查的数字不同,由于.*((?!\1).).*\2
,并尝试重复查找数字。((?!\1).)
检查数字最多重复两次。里面的模式试图匹配一个数字的3个实例。这与问题3有同样的问题,因此编写严格的正则表达式或POSIX正则表达式是不实际的。如果您感到好奇,可能会有(?!.*(.)(?:.*\3){2})
个匹配(允许前导0)。
对于问题3和4,尤其是问题4,检查字符串是否只包含数字更为实际,然后对数字进行排序并在其上循环以检查重复的数字。