剑指offer面试题19:正则表达式匹配(golang实现)

题目描述

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

示例1

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例2

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例3

输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。

示例4

输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。

示例5

输入:
s = "mississippi"
p = "mis*is*p*."
输出: false

Leetcode链接:剑指offer面试题19:正则表达式匹配

算法分析

  • 首先想到的是回溯法,其实就是直接分析每一个状态,从后向前匹配模式串(当前字符:pc)和主串(当前字符:sc)。如果pc == sc,或者pc == “.”,则匹配成功,直接跳过当前字符;若pc == "“时,就要考虑”?"重复0次,1次或者多次的情况。
    递归的终止条件是其中一个字符串已经遍历完,此时若另一个字符串也已经遍历完,说明模式串可以匹配上主串,return true;如果模式串遍历结束,主串尚未遍历完,说明不能匹配,return false;最后是主串遍历结束,模式串尚未遍历完的情况,这种情况比较复杂,详见注释。具体算法为(isMatch1())
  • 后来参考了leetcode的官方题解,完成了动态规划求解。具体算法为(isMatch2())
    要注意的有两个点,一个是dp记录s[:n] 和 p[:m]能否匹配,所以字符串实际索引范围应该为1~len;另一点是为了解决越界问题,双重循环中j从1开始(dp[i][0]必定为false)。

Golang代码如下

func backtrack19(s, p string, i, j int) bool {
	if i < 0 || j < 0 {
		if i < 0 && j < 0 {
			return true
		}
		if j < 0 {
			return false
		}
		if p[j] == '*' {
			//	如果模式串当前字符不是"*",则不可能匹配成功
			for index := j; index >= 0; index = index - 2 {
				//	判断模式串尚未遍历完的部分是不是都是"?*"
 				if p[index] != '*' {
 					//	不满足匹配成功的条件
					return false
				}
			}
			return true
		}
		return false
	} else {
		if s[i] == p[j] || p[j] == '.' {
			return backtrack19(s, p, i-1, j-1)
		}
		if p[j] == '*' {
			if p[j-1] != s[i] && p[j-1] != '.' {
				return backtrack19(s, p, i, j-2)
			}
			//	考虑模板串"?*"重复0次,1次或者多次的情况
			return backtrack19(s, p, i-1, j) || backtrack19(s, p, i-1, j-2) || backtrack19(s, p, i, j-2)
		}
	}
	return false
}

func isMatch1(s string, p string) bool {
	//	判空
	if len(p) == 0 {
		return len(s) == 0
	}
	return backtrack19(s, p, len(s)-1, len(p)-1)
}

func isMatch2(s string, p string) bool {
	if len(p) == 0 {
		return len(s) == 0
	}
	matches := func (i, j int) bool {
		if i == 0 {
			return false
		}
		if s[i-1] == p[j-1] || p[j-1] == '.' {
			return true
		}
		return false
	}
	//	dp记录s[:n] 和 p[:m]能否匹配
	dp := make([][]bool, len(s)+1)
	for i := 0; i <= len(s); i++ {
		dp[i] = make([]bool, len(p)+1)
	}
	dp[0][0] = true
	for i := 0; i <= len(s); i++ {
		//	i > 0 && j == 0 不可能匹配成功
		for j := 1; j <= len(p); j++ {
			if p[j-1] == '*' {
				//	无论是否matches(i, j-1),都要考虑"?*"匹配次数为0的情况
				dp[i][j] = dp[i][j-2]
				if matches(i, j-1) {
					//	"?*"匹配次数加一
					dp[i][j] = dp[i][j] || dp[i-1][j]
				}
			} else if matches(i, j) {
				dp[i][j] = dp[i-1][j-1]
			}
		}
	}
	return dp[len(s)][len(p)]
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值