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

递归解法,​原因都在注释里: ​

public class Solution {
   
   public boolean match(char[] str, char[] pattern) {
   
        return  process(str, pattern, 0, 0);
    }
 
    // 看 str[si...ends] 的字符, 能不能被 pattern[pi...ends] 所匹配
    private boolean process(char[] str, char[] pattern, int si, int pi) {
   
        // 如果 pattern 的字符走完了
        if (pi == pattern.length ) {
   
            // 看 str 字符有没有走完。 没走完就直接返回 false, 走完就是全部匹配完了。。。
            // 注意这里是 可以直接返回结果的~
            return si == str.length;
        }
        
        
        // 如果 pi 来到了最后一个字符; 或者 pi 的下一个字符 不是 *
        if (pi + 1 == pattern.length || pattern[pi + 1] != '*') {
   
            // 看 str 的情况: 
            // str要有字符可以匹配; 
            // pattern 上是 . 或者 pattern和str的字符可以匹配;
            // 都接下去一位,接着看能不能匹配。
            return si != str.length && (str[si] == pattern[pi] || pattern[pi] == '.') 
                && process(str, pattern, si+1, pi+1);
        }
        
        
        // pi 的下一个字符是 * 的情况: 
        // str 要有字符可以匹配,
        // pattern 上是 . 或者 pattern和str的字符可以匹配;
        // * 可以匹配0位或任意位:所以这个while循环就是匹配 0 ... str.length 位的情况,有true直接 返回即可
        while (si != str.length && (str[si] == pattern[pi] || pattern[pi] == '.')) {
   
            if ( process(str, pattern, si, pi+2) ) {
   
                return true;
            }
            si++;
        }
        
        // str 字符走完了, 看 pattern 之后的能不能匹配
        return process(str, pattern, si, pi+2);
    }
}

动态规划解法 :

class Solution {
   
    public boolean isMatch(char[] str, char[] pattern) {
   
        if (str == null && pattern == null) {
   
            return true;
        }
        if (str == null || pattern == null) {
   
            return false;
        }
        int slen = str.length;
        int plen = pattern.length;
        
        // 初始化 dp 矩阵
        boolean[][] dp = initDP(str, pattern);

        // 填充 dp 矩阵
        for (int i =  slen - 1; i >= 0; i--) {
   
            for (int j = plen - 2; j >= 0; j--) {
   
                // pattern 下一位的字符不为 '*' 的情况
                if (pattern[j+1] != '*') {
   
                    // 看当前字符是否匹配, 都走一位后是否匹配
                    dp[i][j] = (str[i] == pattern[j] || pattern[j] == '.') 
                        && dp[i+1][j+1];
                } else {
   
                    // pattern 下一位的字符为 '*' 的情况
                    // * 可以匹配0位或任意位, 依次看匹配 0位、1位 ... 到末尾位的情况
                    int si = i;
                    while (si != slen && (str[si] == pattern[j] || pattern[j] == '.')) {
   
                        if (dp[si][j+2]) {
   
                            dp[i][j] = true;
                            break;
                        }
                        si++;
                    }
                    // str 走完单独算一下, 上面的 str[si] 中会越界
                    if (dp[i][j] == false) {
   
                        dp[i][j] = dp[si][j+2];
                    }
                }
            }
        }

        // 想要的结果
        return dp[0][0];
    }


    // 该 dp 矩阵中, (i, j) 依赖 (i+1, j+1) 和 (i, j+2),(i+1, j+2)...(str.length, j+2) 的位置
    // 所以, 要先把最后 2 列, 最后 1 行, 填充完整。 这样就可以推导出任何一点位置上的值了
    private boolean[][] initDP(char[] str, char[] pattern){
   
        int slen = str.length;
        int plen = pattern.length;
        boolean[][] dp = new boolean[slen+1][plen+1];
        
        // 倒数第一列, 都走完肯定为 true
        dp[slen][plen] = true;  

        // 倒数第二列, 看 pattern 和 str 的最后一个字符相不相等,或者 pattern 上是否为 '.' ; 注意边界问题
        if (slen > 0 && plen > 0 && ( str[slen-1] == pattern[plen-1] || pattern[plen-1] == '.' )) {
   
            dp[slen-1][plen-1] = true;
        }

        // 倒数第一行: str 走完了, 就看 pattern 是否可以匹配。
        for (int j = plen - 2; j >= 0; j = j - 2) {
   
            // pattern 上的字符不是 '*', 并且下一位是 '*' , 才能够匹配得上 ; 
            if (pattern[j] != '*' && pattern[j+1] == '*') {
   
                dp[slen][j] = true;
            } else {
   
                // 只要有 1 处开始匹配不上, 前面的就都不行, 因为pattern后面还有字符无处匹配了。
                break;  
            }
        }

        return dp;
    }
}