正则表达式匹配

120 阅读3分钟

一、题目

本文主要是记录一下写这道题时的理解过程,主要是参考题解后的思考,可能有些地方会不太准确。

image.png

思路分析

先思考一下普通的两个字符串的匹配:两个指针i,j分别在字符串s和字符串p上移动,如果s[i] == p[j],就移动指针,否则匹配失败;也就是说,两个字符的匹配结果只有两种:相等或不相等。 而这道题中新引入了两个符号'.'和'*':

  • '.':可以当做任意单个字符,也就是说在p中遇到'.',当成一个字符处理
  • '*' : 匹配零个/多个前面的那一个元素,假设前面的元素为a,那么a*这个组合可以当做n个a(n>=0)的组合使用

对于这种每次的选择的都有多种的情况,可以采取动态规划穷举的方式:

  1. 定义:dp[i][j] 代表s中长度为i的字符串是否能匹配p中长度为j的字符串
  2. 初始条件:dp[0][0]代表s为空串且p为空串,可以匹配,为true
  3. 状态转移:对于每个dp[i][j],假设dp[..i][..j]已经知道:
  • p.charAt(j)!='*':如果p.charAt(j)=s.charAt(i),那么当下两个字符匹配成功,dp[i][j]是否匹配成功取决于dp[..i-1][..j-1]是否匹配成功,即dp[i][j]==dp[i-1][j-1]
if(p.charAt(j)!='*'){
     if(isMatch(s,i,p,j)){
         dp[i][j]=dp[i-1][j-1];
     }
}
  • p.charAt(j)=='*': 如果前一个字符p.charAt(j-1)和s.charAt(i)相匹配,这个字符可以继续使用,也就是说下标为i和下标为j的两个字符已经匹配成功,可以尝试让该字符继续和s的前一个字符相匹配(dp[i][j]=dp[i-1][j]),也可以不再使用这个字符(dp[i][j]=dp[i][j-2]),只要有一种方式成功即可;当然,如果p.charAt(j-1)!=s.charAt(i),可以直接舍弃这个字符组合了(dp[i][j]=dp[i][j-2])
if(p.charAt(j - 1) == '*'){
      dp[i][j]=dp[i][j-2];
      if(isMatch(s,i,p,j-1)){
         dp[i][j]=dp[i-1][j]||dp[i][j];
   }    
}

代码实现

class Solution {
    public boolean isMatch(String s, String p) {
        int m = s.length();
        int n = p.length();
        // dp[i][j]表示s中的i个字符是否合p中的j个字符匹配
        boolean[][] dp = new boolean[m+1][n+1];
        // 空字符串跟空字符串匹配
        dp[0][0] = true;
        // 注意实际的s p下标从1开始
        for(int i=0;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(p.charAt(j - 1) == '*'){
                    dp[i][j]=dp[i][j-2];
                    if(isMatch(s,i,p,j-1)){
                        dp[i][j]=dp[i-1][j]||dp[i][j];
                    }    
                }else{
                    if(isMatch(s,i,p,j)){
                        dp[i][j]=dp[i-1][j-1];
                    }
                }
            }
        }
        return dp[m][n];

    }

    // 判断两个字符是否相等
    public boolean isMatch(String s, int i, String p, int j){
        // s为空串,p不为空串,匹配失败
        if(i==0){
            return false;
        }
        // '.'当成一个字符 匹配成功
        if(p.charAt(j - 1) == '.'){
            return true;
        }
        return p.charAt(j-1)==s.charAt(i-1);
    }
    
}
  • 时间复杂度:两个循环,O(MN)

  • 空间复杂度:dp数组 O(MN)