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

10. 正则表达式匹配(Java)LeeCode,第1张

解题思路:

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];
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/742466.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-28
下一篇 2022-04-28

发表评论

登录后才能评论

评论列表(0条)

保存