leetcode题解-10. Regular Expression Matching

题意:正则匹配。比较两个字符串是否一致,这里有两个特殊符号 “.” 和 “ ” ,”.”可以匹配单个字符,而” ”可以匹配任意个与前一字符相同的字符。

分析:按照Solution中的思路,有递归和动态规划两种方法。而这道题使用动态规划不仅方便易懂,而且代码也非常整洁。首先看一下伪代码:

leetcode题解-10. Regular Expression Matching_第1张图片

看起来很精简,但是要真正理解,我还是用了一下午的时间。下面我们从头开始说明一下构造dp二维数组的过程。

举例:
s = “aab”
p = “c*a*b”

要采用动态规划方法,肯定就是用空间换时间。那么我们申请一个二维数组dp:

boolean[][] dp = new boolean[s.length()+1][p.length()+1];

dp的初始化:

声明:下文中的索引都是从1开始,如p=c*a*b,那么p[0]=”,p[1]=’c’…..

leetcode题解-10. Regular Expression Matching_第2张图片

首先,初始化第一列。只有dp[0][0] = True,dp[0][j]=False (j = 1,2,3)。因为对于p=”“来说,只有s=”“可以匹配到,其他的s匹配不到。

其次,初始化第一列。第一列的初始化就是对于s=”“来说,哪一个p能匹配到。对上图而言,当j=2和j=4时,能匹配到空字符。值得一提的是,在p中, 不能是第一个出现的字符,所以无需进行检查。

for (int i = 1; i <= p.length(); i++) {
                if (p.charAt(i - 1) == '*') {
                    // * 不能是第一个出现的字符,所以无需进行检查
                    dp[0][i] = dp[0][i - 2];
                }
            }

dp的动态方程:

1、 对于特定的i和j,s和p中的字符能匹配。举例:s=abcd,p=abed,s[4]=p[4]=d,此时s和p是否匹配,就要看前面s[1]….s[3]=abc和p[1]…p[3]=abe是否匹配。此时满足的动态方程就是:

if(s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.'){
      dp[i][j] = dp[i - 1][j - 1];
}

2、对于特定的i和j,s和p中的字符不能匹配,即s[i]!=p[j]并且p[j]!=’.’。但是如果p[j]= ,又会分为如下两种情况:

2.1、当p中 前面的字母与p中的字母不匹配时,即s=abcd,p=abcde ,此时s[4]=d!=s[5]=e,那么s[1]….s[4]=abcd和p[1]….p[6]=abcde 是否匹配,就要看s[1]….s[4]=abcd和p[1]….p[4]=abcd是否匹配。

if(s.charAt(i - 1) != p.charAt(j - 2) && p.charAt(j - 2) != '.'){
                            dp[i][j] = dp[i][j - 2];
                        }

2.2、当p中 前面的字母与p中的字母匹配时,又有三种情况:

2.2.1、
s=abcdd,p=abcd ,s[5]=d,p[5]= 时,s和p是否匹配就要看s[1]….s[4]和p[1]….p[5]是否匹配,即:

dp[i][j] = dp[i - 1][j]

2.2.2、
s=abcd,p=abcd ,s[4]=d,p[5]= 时,s和p是否匹配就要看s[1]….s[4]和p[1]….p[4]是否匹配,即:

dp[i][j] = dp[i][j - 1]

2.2.3、
s=abc,p=abc. ,s[3]=c,p[5]= 时,s和p是否匹配就要看s[1]….s[3]和p[1]….p[3]是否匹配,即:

dp[i][j] = dp[i][j - 2]

对于2.2.1、2.2.2、2.2.3三种情况只要满足一种即可。所以当p中 前面的字母与p中的字母匹配时:

dp[i][j] = dp[i - 1][j] || dp[i][j - 1] || dp[i][j - 2];

通过对上述逻辑的分析,填出dp初始化图中的空白部分应该也不在话下。感兴趣的同学可以自己加断点调试一下。所以总体的代码为:

//isMatch("aa","a") → false
//isMatch("aa","aa") → true
//isMatch("aaa","aa") → false
//isMatch("aa", "a*") → true
//isMatch("aa", ".*") → true
//isMatch("ab", ".*") → true
//isMatch("aab", "c*a*b") → true
class Solution {
     public boolean isMatch(String s, String p){
         if (s == null || p == null) {
                return false;
            }
            boolean[][] dp = new boolean[s.length()+1][p.length()+1];
            // 初始化 先初始化第一列
            dp[0][0] = true;
            // 在初始化第一行
            for (int i = 1; i <= p.length(); i++) {
                if (p.charAt(i - 1) == '*') {
                    // * 不能使第一个出现的字符,所以无需进行检查
                    dp[0][i] = dp[0][i - 2];
                }
            }
            for (int i = 1 ; i <= s.length(); i++) {
                for (int j = 1; j <= p.length(); j++) {
                    if(s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.'){
                        dp[i][j] = dp[i - 1][j - 1];
                    }else if(p.charAt(j - 1) == '*'){
                        if(s.charAt(i - 1) != p.charAt(j - 2) && p.charAt(j - 2) != '.'){
                            dp[i][j] = dp[i][j - 2];
                        }else{
                            dp[i][j] = dp[i - 1][j] || dp[i][j - 1] || dp[i][j - 2];
                        }
                    }
                }
            }
            return dp[s.length()][p.length()];
        }
    public static void main(String[] args) {
        String s = "f";
        String p = "f.*";
        Solution sl = new Solution();
        System.out.println(sl.isMatch(s, p));
    }

}

你可能感兴趣的:(Leetcode题解)