1. 程式人生 > 實用技巧 >[LeetCode] 10. 正則表示式匹配

[LeetCode] 10. 正則表示式匹配

給你一個字串 s 和一個字元規律 p,請你來實現一個支援 '.' 和 '*' 的正則表示式匹配。

'.' 匹配任意單個字元
'*' 匹配零個或多個前面的那一個元素
所謂匹配,是要涵蓋 整個 字串 s的,而不是部分字串。

說明:

s 可能為空,且只包含從 a-z 的小寫字母。
p 可能為空,且只包含從 a-z 的小寫字母,以及字元 . 和 *。

輸入:
s = "aa"
p = "a"
輸出: false
解釋: "a" 無法匹配 "aa" 整個字串。

輸入:
s = "aa"
p = "a*"
輸出: true
解釋: 因為 '*' 代表可以匹配零個或多個前面的那一個元素, 在這裡前面的元素就是 'a'。因此,字串 "aa" 可被視為 'a' 重複了一次。

輸入:
s = "ab"
p = ".*"
輸出: true
解釋: ".*" 表示可匹配零個或多個('*')任意字元('.')。

輸入:
s = "aab"
p = "c*a*b"
輸出: true
解釋: 因為 '*' 表示零個或多個,這裡 'c' 為 0 個, 'a' 被重複一次。因此可以匹配字串 "aab"。

輸入:
s = "mississippi"
p = "mis*is*p*."
輸出: false

分析

  1. 動態規劃

解法

解法一

  • 動態規劃

  • dp[i][j]表示s的前i個字元dp和p的前j個字元匹配

    狀態轉移方程:

    第j個字元為字母:dp[i][j] = dp[i-1][j-1] && s[i] = p[j]

    第j個字元為'.'(匹配任意單個字元):s[i] = p[j]一定成立

    第j個字元為'*'(匹配零個或多個前面的那一個元素)考慮最新一次匹配情況:

    • 不匹配(...[a]和...[a]b*):dp[i][j] = dp[i][j-2] ,依賴上方元素

    • 匹配(...a[b]和...a[b]*,...ab[b]和...a[b]*):最新一次匹配時有dp[i][j] = dp[i-1][j] && s[i] = p[j-1],依賴左方元素,看之前是否匹配,及最新一次匹配的字元是否匹配

  • 時間複雜度O(\(mn\))

  • 空間複雜度O(\(mn\))

class Solution {
    public boolean isMatch(String s, String p) {
        int sl = s.length();
        int pl = p.length();
        // 需考慮s和p長度為0的情況
        boolean[][] dp = new boolean[sl + 1][pl + 1];
        // 初始化s和p長度為0的情況
        dp[0][0] = true;
        // 初始化s長度為0的情況,dp[0][2]->dp[0][p.length()],p長度為0預設為false,若後面處理還要考慮越界
        for(int k = 2; k < pl + 1; k++){
            dp[0][k] = p.charAt(k - 1) == '*' && dp[0][k - 2];
        }
        for(int i = 1; i < sl + 1; i++){
            for(int j = 1; j < pl + 1; j++){
                if(p.charAt(j - 1) == '*'){// 字元為*的情況
                    // 先考慮最新一次不匹配的情況
                    dp[i][j] = dp[i][j-2]||(dp[i - 1][j] && matches(s, p, i-1, j-2));
                }
                else {// 字元不為*的情況
                    dp[i][j] = dp[i -1][j -1] && matches(s, p, i - 1, j - 1);
                }
            }
        }
        // 最終匹配情況
        return dp[sl][pl];
    }

    // 判斷字元是否匹配,m,n為字串索引
    public boolean matches(String s, String p, int m, int n){
        return (p.charAt(n) == '.') || s.charAt(m) == p.charAt(n);
    }
}