[LeetCode] 10. 正則表示式匹配
阿新 • • 發佈:2020-09-08
給你一個字串 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
分析
- 動態規劃
解法
解法一
- 動態規劃
-
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); } }