Python实现通配符匹配:递归程序设计艺术(3)

如果说上节的兔子问题还可以用非递归的方法实现的话,那么下面这个例子就很难用非递归方法来实现了。

假设“*”可以匹配0个或0个以上的字符,“?”可以匹配且仅匹配一个字符。请写一个递归函数match(pattern, str)判断字符串str是否与模式pattern匹配。

比如,在操作系统里寻找一个文件时,不必写全文件的名字。可以使用*和?这两个通配符。比如AB*C?.doc表示任何以AB打头,倒数第二个字符是C的doc文档。

我们仍然按照递归三部曲来考虑这个问题的解法。

第一,确定边界条件。

在什么情况下我们可以直接判断str是否与pattern匹配?显然,如果str是个空字符串(即长度为0的字符串)的话,那pattern也必须是个空字符串两者才能匹配。反过来,如果pattern是个空字符串的话,那它也只能和空字符串匹配。所以这个边界条件是str或pattern是空字符串。

第二,递归假设。

假设在pattern或者str比原来少一个字符情况下match函数总能正确地判定二者是否匹配。注意递归假设所用到的参数要比原参数更靠近边界,只要满足这个条件,任何假设都是合理的。

第三步,递归推导。

我们可以考虑pattern的第一个字符first。

  1. 如果它是普通字符,看它是否与str的第一个字符相等,如果不等意味着pattern和str不匹配;如果相等,则还要看pattern的剩余部分与str的剩余部分是否匹配。
  2. 如果first是字符?,则str的第一个字符与它匹配,只需考虑pattern和str各自的剩余部分是否匹配即可。
  3. 如果first是字符*,则又分为两种情况考虑:
  1. 字符*只匹配0个字符,这意味着我们可以把*删除,然后考虑pattern剩下的部分是否与str匹配即可。
  2. 字符*匹配1个或1个以上的字符,这意味着str除了第一个字符以外,还可以有0个或0个以上的字符与*匹配。所以,可以把str的第一个字符删除,然后看它剩下的部分与pattern是否匹配即可。

综上所述,给出Python递归代码如下:

解决字符串匹配问题的递归程序

def match(s, p):
    if len(p) == 0:
        return len(s) == 0

    first = p[0]
    if first == '?':
        return len(s) > 0 and match(s[1:], p[1:])
    if first == '*':
        return match(s, p[1:]) or len(s) > 0 and match(s[1:], p)
    return len(s) > 0 and first == s[0] and match(s[1:], p[1:])

def _test_match(s, p, result):
    print('%s, %s, %s, %s' % (s, p, result, match(s, p)))

if __name__ == '__main__':
    _test_match('ababaab', 'a*b', True)
    _test_match('ababaab', '*abab*', True)
    _test_match('ababaab', 'a*a?b', True)

    _test_match('ababaab', 'a*bb', False)
    _test_match('ababaab', 'aabab*', False)
    _test_match('ababaab', 'a*b?b', False)

_test_match()函数名以下划线打头,这意味着这是一个私有方法,不能被其他Python文件通过import或者from语句引用。

代码中使用了形如s[1:]的语法,表示取s从第二个字符开始直到最后的所有字符构成的新字符串。比如"abcde"[1:]的结果是"bcde",Python把字符串看成是字符的列表,所以可以用求子列表的办法获得子串,再如:"abcde"[2:4]和"abcde"[:4]的结果分别是"cd"和"abcd"。而在C、C++、Java中要取一个子串则必须调用内部函数。代码运行结果如下:

ababaab, a*b, True, True
ababaab, *abab*, True, True
ababaab, a*a?b, True, True
ababaab, a*bb, False, False
ababaab, aabab*, False, False
ababaab, a*b?b, False, False
​​​​​​​

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

方林博士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值