JZ52 正则表达式匹配

描述

请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配’

正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符")。

 一般利用正则表达式进行字符串操作。

正则表达式教程—https://www.runoob.com/regexp/regexp-tutorial.html

https://www.nowcoder.com/practice/28970c15befb4ff3a264189087b99ad4?tpId=13&tags=&title=&difficulty=0&judgeStatus=0&rp=1

来自官方的详细题解,虽然之前的JZ49有看到正则表达式,但是关于正则表达式的匹配问题还是较难理解。

首先是递归解题思路。

先来看一下pattern长度只有一位的情况下的匹配:

True

Pattern

0(索引下标)

a

Str

0(索引下标)

a

匹配

True

Pattern

0(索引下标)

.

Str

0(索引下标)

a

匹配

False

Pattern

0(索引下标)

b

Str

0(索引下标)

a

不匹配

所以递归的终止条件就是str[0] = pattern[0](记为first_match)。

然后继续看pattern长度为两位的时候,并且pattern[1]=*时,结合三个示例看:

False

Pattern

0(索引下标)

1

b

*

Str

0(索引下标)

a

不匹配

1

a

匹配

True

Pattern

0(索引下标)

1

a

*

Str

0(索引下标)

a

匹配

1

a

匹配

True

Pattern

0(索引下标)

1

.

*

Str

0(索引下标)

a

匹配

1

b

匹配

综上,可以看到只有上文提到的str[0] = pattern[0],也就是first_match满足时,并且str[1]=pattern[1]时,正则匹配为True。

保持pattern[1]=*不变,来看一下len(pattern)>2的情况:

False

Pattern

0(索引下标)

1

2

a

*

b

Str

0(索引下标)

a

匹配

1

a

匹配

2

a

不匹配

False

Pattern

0(索引下标)

1

2

a

*

a

Str

0(索引下标)

b

不匹配

1

a

匹配

2

a

匹配

True

Pattern

0(索引下标)

1

2

.

*

a

Str

0(索引下标)

a

匹配

1

a

匹配

2

a

匹配

综上,所需要的条件仍然是str[0] = pattern[0],并且str[1:] = pattern才成立(因为上面那种情况是len(pattern)=2,这里长度变了,所以后面需要匹配的条件也变了)。

但是所有情况都需要str[0] = pattern[0]吗?作者还有另外一种示例:

False

Pattern

0(索引下标)

1

2

3

4

c

*

a

*

b

Str

0(索引下标)

a

不匹配

1

a

匹配

2

b

不匹配

3

4

True

Pattern

0(索引下标)

1

2

3

4

c

*

a

*

b

Str

0(索引下标)

1

2

a

匹配

3

a

匹配

4

b

匹配

所以另外一种也可以成功匹配的方法就不局限于str[0] = pattern[0]了。

刚刚看到了pattern[1]=’*’的情况,再来看patterb[1]<>’*’,意思是星号在索引1之后的任意位置,因为索引1之后,所以len(pattern)>2。

False

Pattern

0(索引下标)

1

2

3

a

b

*

a

Str

0(索引下标)

a

匹配

1

a

不匹配

2

a

匹配

True

Pattern

0

1

2

0(索引下标)

1

2

3

4

5

6

a

b

*

a

c

*

a

Str

0(索引下标)

a

匹配

1

a

匹配

2

a

匹配

(另外https://blog.nowcoder.net/n/ca8faf039491463b97e3a2e5bda0bfed?f=comment)这里需要知道一些补充知识:如果str[0]=pattern[0],可以有3种匹配方式:

  1. pattern后移2字符,相当于x*被忽略(也就是为什么上面这个表格原本的1,2,4,5列被忽略的原因)
  2. str后移1字符,pattern后移2字符; 相当于x*算一次
  3. str后移1字符,pettern不变,即继续匹配str下一位,因为*可以匹配多位,相当于算多次
class Solution:
    def match(self , str , pattern ):
        if not pattern:                   #1.特殊情况,不存在匹配模式,那么就没有匹配字符串
            return not str
         #2. 递归的终止条件f(1) = 1:在这里就是 首位即匹配。
        first_match = str and pattern[0] in {str[0], '.'}    

        #如果模式长度 >= 2,并且 模式索引[1] == '*'情况,也要分两种:
        if len(pattern) >= 2 and pattern[1] == '*':
            #第一种就是模式长度>2的情况下:字符串完全匹配从模式索引2之后的位置
            return (self.match(str, pattern[2:]) or
                    #或者模式长度 =2的情况下:字符串从索引1位置开始,完全匹配模式
                    first_match and self.match(str[1:], pattern))
        else:
        #否则,模式长度>=2,而模式索引从1开始是 点字符或 *字符在其他位置,
            return first_match and self.match(str[1:], pattern[1:])

还有另外一种动态规划的解法:

动态规划其实在剑指里面用的很多,包括前面的斐波那契数列等

来自同一个作者的题解:

  • 定义一个f[i][j]的状态转移方程,其中i 表示str中的第i个字符;j表示pattern中的第j个字符,然后判断是否匹配。
  • 接着我们需要判断两种情况,第一种是当i、j指向的字符是同一个字母/点号(".")的时候,我们只需要判断对应位置的字符是否相同,相同则转移状态去判断子问题f[i-1][j-1]是否匹配。
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param str string字符串 
# @param pattern string字符串 
# @return bool布尔型
#
class Solution:
    def match(self , str , pattern ):
        m, n = len(str), len(pattern)   # 分别找到str和pattern的长度

        def matches(i, j):              #定义一个转移方程函数
            if i == 0:                  #首先考虑一种特殊情况: str为空;
           #否则第一种是当i、j指向的字符是同一个字母/点号(".")的时候,我们只需要判断对应位置的字符是否相同,
           #相同则转移状态去判断子问题f[i-1][j-1]是否匹配
                return False
            if pattern[j - 1] == '.':
                return True
            return str[i - 1] == pattern[j - 1]

        f = [[False] * (n + 1) for _ in range(m + 1)]
        f[0][0] = True             #动态规划的边界条件为f[0][0]=true,即两个空字符串是可以匹配的
        for i in range(m + 1):
            for j in range(1, n + 1):
                #判断当j 指向 * 号的时候两种情况:
                if pattern[j - 1] == '*':
                    f[i][j] |= f[i][j - 2]
                    if matches(i, j - 1):
                        f[i][j] |= f[i - 1][j]
                else:
                    if matches(i, j):
                        f[i][j] |= f[i - 1][j - 1]
        return f[m][n]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值