iT邦幫忙

2025 iThome 鐵人賽

DAY 0
0
自我挑戰組

LeetCode 每日一題挑戰系列 第 1

LeetCode 鐵人賽 Day 9 — Regular Expression Matching

  • 分享至 

  • xImage
  •  

題目簡介

題目要我們自己實作一個簡化版的正則表達式比對。給定一個字串 s 和一個模式 p,規則如下:

. 可以匹配任何一個字元

  • 可以匹配前一個字元零次或多次

要求:必須完全匹配整個字串,而不是部分比對。

例子:

s = "aa", p = "a" → false

s = "aa", p = "a*" → true

s = "ab", p = ".*" → true

https://ithelp.ithome.com.tw/upload/images/20250923/201695374pS33hu4y8.pnghttps://ithelp.ithome.com.tw/upload/images/20250923/20169537i1UneMjyLb.pnghttps://ithelp.ithome.com.tw/upload/images/20250923/201695370H0LEUcHu2.pnghttps://ithelp.ithome.com.tw/upload/images/20250923/20169537lo0qTeQ3Gf.png 思路

這題難度在於 * 的靈活性:

a* 可以代表 ""(0 次)或 "a", "aa", "aaa"...

所以遇到 *,要考慮「跳過前一個字元」或「繼續匹配前一個字元」。

解法有幾種:

遞迴 (回溯) → 實現容易,但效率差。

動態規劃 (DP) → 最佳解,時間複雜度 O(m × n),其中 m, n 分別是 s 和 p 的長度。

DP 思路:

定義 dp[i][j] 表示 s[0..i-1] 是否能和 p[0..j-1] 匹配。

遞推關係:

如果 p[j-1] 是一般字元或 .:
dp[i][j] = dp[i-1][j-1] && (s[i-1] == p[j-1] 或 p[j-1] == '.')

如果 p[j-1] == '*':

忽略 * 和前一個字元:dp[i][j] = dp[i][j-2]

或者繼續匹配:dp[i][j] = dp[i-1][j] && (s[i-1] == p[j-2] 或 p[j-2] == '.')

程式碼 (Java)
class Solution {
public boolean isMatch(String s, String p) {
int m = s.length(), n = p.length();
boolean[][] dp = new boolean[m + 1][n + 1];

    // 空字串匹配空模式
    dp[0][0] = true;

    // 初始化:處理像 a*, a*b*, a*b*c* 這種能匹配空字串的情況
    for (int j = 2; j <= n; j++) {
        if (p.charAt(j - 1) == '*') {
            dp[0][j] = dp[0][j - 2];
        }
    }

    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            char sc = s.charAt(i - 1);
            char pc = p.charAt(j - 1);

            if (pc == '.' || pc == sc) {
                dp[i][j] = dp[i - 1][j - 1];
            } else if (pc == '*') {
                // 忽略 * 和它前面的字元
                dp[i][j] = dp[i][j - 2];

                // 或者用 * 來匹配多個前字元
                char prev = p.charAt(j - 2);
                if (prev == '.' || prev == sc) {
                    dp[i][j] = dp[i][j] || dp[i - 1][j];
                }
            }
        }
    }

    return dp[m][n];
}

}

測試
Input: s = "aa", p = "a" → false
Input: s = "aa", p = "a*" → true
Input: s = "ab", p = "." → true
Input: s = "mississippi", p = "mis
isp." → false
Input: s = "mississippi", p = "misisip*." → true

心得

這題是名副其實的 Hard,我覺得難點主要在:

你要很清楚「*」代表什麼意思(零次 or 多次)。

DP 的狀態轉移要寫對,不然會有一堆 corner case。

這題做完感覺像打了一場硬仗,但也讓我更熟悉了 DP 的思考方式,特別是「定義狀態 → 找轉移」


下一篇
Day 1:解決 Two Sum 問題(Java 範例)
系列文
LeetCode 每日一題挑戰9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言