正则表达拼图

时间:2010-09-11 23:43:35

标签: regex puzzle

这不是家庭作业,而是旧的考试问题。我很想知道答案。

我们给出一个字母S = {0,1,2,3,4,5,6,7,8,9,+}。将语言L定义为此字母表中的字符串集合w,使得w在L中,如果:

a)w是一个数字,如42 w是(有限)数字之和,如34 + 16或34 + 2 + 10

b)由w表示的数字可以被3整除。

为L编写正则表达式(和DFA)。

3 个答案:

答案 0 :(得分:6)

这应该有效:

^(?:0|(?:(?:[369]|[147](?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147]0*(?:\+?(?:0\
+)*[369]0*)*\+?(?:0\+)*[258])*(?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[258]|0*(?:
\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147]0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147])|[
258](?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[258]0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0
\+)*[147])*(?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147]|0*(?:\+?(?:0\+)*[369]0*)
*\+?(?:0\+)*[258]0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[258]))0*)+)(?:\+(?:0|(?:(?
:[369]|[147](?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147]0*(?:\+?(?:0\+)*[369]0*)
*\+?(?:0\+)*[258])*(?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[258]|0*(?:\+?(?:0\+)*
[369]0*)*\+?(?:0\+)*[147]0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147])|[258](?:0*(?
:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[258]0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147])*
(?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147]|0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)
*[258]0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[258]))0*)+))*$

它的工作原理是有三个状态代表到目前为止模数为3的数字之和。它不允许在数字上加前导零,在字符串的开头和结尾加上符号,以及两个连续的加号。

生成正则表达式和试验台:

a = r'0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*'
b = r'a[147]'
c = r'a[258]'

r1 = '[369]|[147](?:bc)*(?:c|bb)|[258](?:cb)*(?:b|cc)'
r2 = '(?:0|(?:(?:' + r1 + ')0*)+)'
r3 = '^' + r2 + r'(?:\+' + r2 + ')*$'
r = r3.replace('b', b).replace('c', c).replace('a', a)

print r

# Test on 10000 examples.

import random, re
random.seed(1)
r = re.compile(r)
for _ in range(10000):
    x = ''.join(random.choice('0123456789+') for j in range(random.randint(1,50)))
    if re.search(r'(?:\+|^)(?:\+|0[0-9])|\+$', x):
        valid = False
    else:
        valid = eval(x) % 3 == 0
    result = re.match(r, x) is not None
    if result != valid:
        print 'Failed for ' + x

答案 1 :(得分:2)

请注意,我对DFA语法的记忆已经过时了,所以我的回答无疑有点破碎。希望这能为您提供一个总体思路。我选择完全忽略+。正如AmirW所说,abc + def和abcdef在可分割性方面是相同的。

接受状态是C。

A=1,4,7,BB,AC,CA  
B=2,5,8,AA,BC,CB  
C=0,3,6,9,AB,BA,CC

请注意,上述语言使用了所有9种可能的ABC配对。它总是以A,B或C结束,并且每个变量使用都是成对的这一事实意味着每次迭代处理都会缩短变量字符串。

示例:

1490 = AACC = BCC = BC = B (Fail)  
1491 = AACA = BCA = BA = C  (Success)

答案 2 :(得分:1)

不是一个完整的解决方案,只是一个想法:

(B)单独:“加号”标志在这里无关紧要。 abc + defabcdef相同,为了除以3的可分性。对于后一种情况,这里有一个正则表达式:http://blog.vkistudios.com/index.cfm/2008/12/30/Regular-Expression-to-determine-if-a-base-10-number-is-divisible-by-3

将其与要求(A)相结合,我们可以采用(B)的解决方案并对其进行修改:

  • 首先读取的字符必须是0..9(不是加号)

  • 输入不能以加号结尾,因此:复制每个州(将S用于原始状态,S'用于复制以区分它们)。如果我们处于州S并且我们读了一个加号,我们将转移到S'

  • 在阅读数字时,我们将进入新状态,就好像我们在SS'个州不能接受(另一个)加号。

  • 此外,即使S'S也不是“接受状态”。 (因为输入不能以加号结尾)。