10. 正则表达式匹配(Java)LeeCode

在这里插入图片描述

解题思路

1. 如何匹配

从左往右匹配,要考虑后面是否是 * 情况会多一些。

但从右往左匹配,* 只影响前面的一个字符。

所以我们从右往左匹配。

2. 要考虑几种情况?

这道题我选择了动态规划的思路,当然还有其他方法像利用自动机,本人能力有限,这里就先不提及其他方法了。

对于动态规划,一般来讲我们要找到 base case 和状态转移方程。

想象一下,两个 index 分别从两个字符末尾往前匹配,我们会得到一 dp 表。
很多动态规划问题都是求 dp 表。

dp 表的一维度长度是文本串的长度。
dp 表的二维长度是匹配模式串的长度。

char[] arr_s = s.toCharArray();
char[] arr_p = p.toCharArray();

// dp[i][j]:表示s的前i个字符,p的前j个字符
boolean[][] dp = new boolean[arr_s.length + 1][arr_p.length + 1];

1. 先来看下 base case

从右往左匹配,如果都能匹配上那两个字符串的零位都相等,dp[0][0] 等于 true。

// s为空,p为空,能匹配上
dp[0][0] = true;

文本串为空,模式串不为空。

这里考虑一下末尾第一个字符是 * 的问题

        // s为空,p不为空,由于*可以匹配0个字符,所以有可能为true
        //p 往前再看一位,也就是[j - 2]
        for (int j = 1; j <= arr_p.length; j++) {
            if (arr_p[j - 1] == '*') {
                dp[0][j] = dp[0][j - 2];
            }
        }

文本串不为空,模式串为空。

这种情况必为false(boolean数组默认值为false,所以这里不需要操作)

2. 状态转移方程

这里我没有用公式列出,而是直接用代码逻辑方式分析。

总体来说,两种情况:

  • 末尾字符能匹配上
  • 末尾字符不能匹配上
  • 末尾字符串是星号

末尾都能匹配上,也分两种情况,文本串与模式串各自最后一位确实一致。
也可能模式串的最后一位是 “ . ”
(根据 dp 表的定义,arr_s[i - 1] 和 arr_p[j - 1] 是各自数组的最后一位。)

if (arr_s[i - 1] == arr_p[j - 1] || arr_p[j - 1] == '.')   
	  dp[i][j] = dp[i - 1][j - 1];

末尾字符不能匹配上,不能匹配上也就是两个数组末尾两个字符串不一致,这种情况 dp 表不转移。

末尾字符串为星号。

此时我们要考虑 模式串星号前面的一个字符能否与文本串匹配。

如果能匹配上,我们就要考虑,星号是匹配零次还是多次。

如果模式串末尾星号前一个字符不能与文本串匹配,如下

这里注意下,如果模式串星号的前一个字符不能与文本串匹配,还有机会!
向前再移动一位。

例如, * a * 这种情况

if (arr_p[j - 1] == '*') { 
	// 模式串*的前一个字符能够跟文本串的末位匹配上,arr_p[j - 2] 表示模式串的前一个字符。
	//此时前一个字符也可能是 ‘.’,所以也是能匹配上的情况
	if (arr_s[i - 1] == arr_p[j - 2] || arr_p[j - 2] == '.') {
		dp[i][j] = dp[i][j - 2]      // 星号匹配0次的情况(就等于dp[i][j - 2] 本身)
        || dp[i - 1][j];     		// 星号匹配1次或多次的情况
     else { // 模式串*的前一个字符不能够跟文本串的末位匹配
           dp[i][j] = dp[i][j - 2];     // 向前再看一位,如果前一位是还是星号依然有机会
       }

完整代码:

class Solution {
    public boolean isMatch(String s, String p) {
        char[] arr_s = s.toCharArray();
        char[] arr_p = p.toCharArray();

        // dp[i][j]:表示s的前i个字符,p的前j个字符是否能够匹配
        boolean[][] dp = new boolean[arr_s.length + 1][arr_p.length + 1];

 
        // s为空,p为空,能匹配上
        dp[0][0] = true;
        // p为空,s不为空,必为false(boolean数组默认值为false,无需处理)

        // s为空,p不为空,由于*可以匹配0个字符,所以有可能为true
        for (int j = 1; j <= arr_p.length; j++) {
            if (arr_p[j - 1] == '*') {
                dp[0][j] = dp[0][j - 2];
            }
        }

        
        for (int i = 1; i <= arr_s.length; i++) {
            for (int j = 1; j <= arr_p.length; j++) {
                // 文本串和模式串末位字符能匹配上
                if (arr_s[i - 1] == arr_p[j - 1] || arr_p[j - 1] == '.') {
                    dp[i][j] = dp[i - 1][j - 1];
                } else if (arr_p[j - 1] == '*') { // 模式串*的前一个字符能够跟文本串的末位匹配上,arr_p[j - 2] 表示模式串的前一个字符。
	//此时前一个字符也可能是 ‘.’,所以也是能匹配上的情况
                    if (arr_s[i - 1] == arr_p[j - 2] || arr_p[j - 2] == '.') {
                        dp[i][j] = dp[i][j - 2]     // 星号匹配0次的情况(就等于dp[i][j - 2] 本身)
                                || dp[i - 1][j];     // 星号匹配1次或多次的情况
                    } else { // 模式串*的前一个字符不能够跟文本串的末位匹配
                        dp[i][j] = dp[i][j - 2];     // 向前再看一位,如果前一位是还是星号依然有机会
                    }
                }
            }
        }
        return dp[arr_s.length][arr_p.length];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Not_Today.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值