剑指 Offer 19. 正则表达式匹配 + 动态规划

剑指 Offer 19. 正则表达式匹配

题目链接

一. 字符串匹配大致可以分为三种情况:

  1. 第一种:正则串的最后一个字符为正常字符,此时根据主串的最后一个字符是否和它相同来判断是否匹配,
    如果相同,则看A[N-2]和B[M-2]是否匹配。
  2. 第二种:正则串的最后一个字符为【.】表示可以匹配任意一个字符,此时直接看A[N-2]和B[M-2]是否匹配。
  3. 第三种:正则串的最后一个字符为【*】表示匹配0或任意个数的B[M-2]。这又可以分为两种情况:
    • 如果主串的最后一个字符不是B[M-2],则说明此时匹配的是0个B[M-2],只需要看A[N-1]和B[M-3](跳过了正则串的后两个字符)
    • 如果主串的最后一个字符是B[M-2], 此时主串往前移动一个字符,表示匹配成功,但是正则串不动(可能*没有全部匹配成功)。

二. 转移方程:f[i][j]表示A主串的前i个字符是否可以和B正则串的前j个字符相匹配。

  1. 第一种情况和第二种情况可以合并在一起:
    f[i][j] = f[i-1][j-1]
  2. 第三种情况的转义方程如下所示:
    • 如果主串的最后一个字符不是B[M-2],直接不看正则串的后两个字符:f[i][j] = f[i][j-2]
    • 否则,正则串不动,主串往前移动一个字符:f[i][j] = f[i-1][j]

三. 初始值的设定:

  1. 主串和正则串都为空串,两者匹配:f[0][0] = true;
  2. 主串不为空,正则串为空,则必不匹配: f[i][0] = false;
  3. 主串为空,正则串不为空,此时需要进行判断,无法赋予初始值。
package com.walegarrett.offer;

/**
 * @Author WaleGarrett
 * @Date 2020/12/10 20:14
 * 题目链接:https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/
 */

/**
 * 一. 字符串匹配大致可以分为三种情况:
 * 第一种:正则串的最后一个字符为正常字符,此时根据主串的最后一个字符是否和它相同来判断是否匹配,
 *         如果相同,则看A[N-2]和B[M-2]是否匹配。
 * 第二种:正则串的最后一个字符为【.】表示可以匹配任意一个字符,此时直接看A[N-2]和B[M-2]是否匹配。
 * 第三种:正则串的最后一个字符为【*】表示匹配0或任意个数的B[M-2]。这又可以分为两种情况:
 *      1. 如果主串的最后一个字符不是B[M-2],则说明此时匹配的是0个B[M-2],只需要看A[N-1]和B[M-3](跳过了正则串的后两个字符)
 *      2. 如果主串的最后一个字符是B[M-2], 此时主串往前移动一个字符,表示匹配成功,但是正则串不动(可能*没有全部匹配成功)。
 *
 * 二. 转移方程:f[i][j]表示A主串的前i个字符是否可以和B正则串的前j个字符相匹配。
 * 1. 第一种情况和第二种情况可以合并在一起:
 *      f[i][j] = f[i-1][j-1]
 * 2. 第三种情况的转义方程如下所示:
 *      2.1 如果主串的最后一个字符不是B[M-2],直接不看正则串的后两个字符:f[i][j] = f[i][j-2]
 *      2.2 否则,正则串不动,主串往前移动一个字符:f[i][j] = f[i-1][j]
 *
 * 三. 初始值的设定:
 * 1. 主串和正则串都为空串,两者匹配:f[0][0] = true;
 * 2. 主串不为空,正则串为空,则必不匹配: f[i][0] = false;
 * 3. 主串为空,正则串不为空,此时需要进行判断,无法赋予初始值。
 */
public class Offer_19 {
    public boolean isMatch(String s, String p) {
        int lens = s.length();
        int lenp = p.length();
        boolean[][] dp = new boolean[lens+1][lenp+1];
        for(int i = 0; i <= lens; i++){
            for(int j = 0; j <= lenp; j++){
                //正则串为空
                if(j == 0){
                    //主串是否为空
                    dp[i][j] = (i == 0);
                    continue;
                }
                // 正则串此时位置是否为【*】字符
                if(p.charAt(j-1) != '*'){
                    if(i > 0 && (s.charAt(i-1) == p.charAt(j-1) || p.charAt(j-1) == '.')){
                        dp[i][j] = dp[i-1][j-1];
                    }

                }else{//正则串最后字符不包含*,则按照前两种情况处理
                    if(j > 1) {
                        dp[i][j] |= dp[i][j - 2];
                    }

                    if(i > 0 && j > 1 && (s.charAt(i-1) == p.charAt(j-2) || p.charAt(j-2) == '.')){
                        dp[i][j] |= dp[i-1][j];
                    }
                }
            }
        }
        return dp[lens][lenp];
    }
}

class Solution {
    public boolean isMatch(String s, String p) {
        int n = s.length();
        int m = p.length();
        boolean[][] dp = new boolean[n+1][m+1];
        dp[0][0] = true;
        for(int i=0; i<=n; i++){// 注意i从0开始,因为这里需要匹配空串的情况
            for(int j=1; j<=m; j++){
                if(p.charAt(j-1) == '*'){
                    // 不与'x*'进行匹配
                    dp[i][j] = dp[i][j-2];

                    if(isMatches(s, p, i, j-1)){
                        // 往前面的字符进行匹配,看看是否也能匹配成功
                        dp[i][j] = dp[i-1][j] || dp[i][j];
                    }
                } else if(isMatches(s, p, i, j)){
                    dp[i][j] = dp[i-1][j-1];
                }
            }
        }
        return dp[n][m];
    }

    private boolean isMatches(String s, String p, int i, int j){
        if(i == 0 && j==0){
            return true;
        }
        if(i == 0 || j == 0){// i== 0表示s是空串,这个时候一定无法匹配
            return false;
        }
        if(p.charAt(j-1) == '.'){
            return true;
        }
        return s.charAt(i-1) == p.charAt(j-1);
    }
}
posted @ 2020-12-12 08:33  Garrett_Wale  阅读(91)  评论(0编辑  收藏  举报