10. Regular Expression Matching
阿新 • • 發佈:2020-09-10
問題:
給定一個字串s,和一個模式串p,求p是否能匹配s
正則表達支援以下兩種符號
'.' Matches any single character. '*' Matches zero or more of the preceding element.
Note: s could be empty and contains only lowercase letters a-z. p could be empty and contains only lowercase letters a-z, and characters like . or *. Example 1: Input: s = "aa" p = "a" Output: false Explanation: "a" does not match the entire string "aa". Example 2: Input: s = "aa" p = "a*" Output: true Explanation: '*' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa". Example 3: Input: s = "ab" p = ".*" Output: true Explanation: ".*" means "zero or more (*) of any character (.)". Example 4: Input: s = "aab" p = "c*a*b" Output: true Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches "aab". Example 5: Input: s = "mississippi" p = "mis*is*p*." Output: false
解法:DP(動態規劃)
1.確定【狀態】:
- 字串s的第i個字元:s[i]
- 匹配串第j個字元:p[j]
2.確定【選擇】:dp[i][j] 分3種情況
- s[i] == p[j] 或者 p[j] == '.' :該字元匹配上
- 前一個子串狀態: =dp[i-1][j-1]
- p[j] == '*':該字元可能匹配上"#*"(#為某一字元p[j-1]),分以下2種情況
- p[j-1]這個字元不匹配當前s[i],即p[j-1]!=s[i] && p[j-1]!='.'
- 則要使該字元匹配,"#*"必定與字串s匹配0次(匹配串的 j 跳過兩個字元#*):=dp[i][j-2]
- p[j-1]這個字元匹配當前s[i],則又分為以下三種"#*"的匹配情況,他們之間求OR:
- 匹配0次(匹配串的 j 跳過兩個字元#*,字串的 i 跳過0個字元):=dp[i][j-2]
- 匹配1次(匹配串的 j 跳過兩個字元#*,字串的 i 跳過一個字元):=dp[i-1][j-2]
- 匹配>1次(匹配串的 j 跳過0個字元,字串的 i 跳過一個字元):=dp[i-1][j]
- 該字元未匹配
- false
3. dp[i][j]的含義:
字串s的0~第 i 個字元,是否能被匹配串p的0~第 j 個字元,匹配上。
4. 狀態轉移:
dp[i][j]=
- (s[i] == p[j] 或者 p[j] == '.'):=前一個子串狀態:dp[i-1][j-1]
- (p[j] == '*'):
- 前一個字元#不匹配s當前字元(p[j-1]!=s[i] && p[j-1]!='.')
- 則使#*匹配0次:dp[i][j-2]
- 前一個字元#匹配s當前字元,OR {
- 使#*匹配0次:dp[i][j-2]
- 使#*匹配1次:dp[i-1][j-2]
- 使#*匹配>1次:dp[i-1][j] }
- 前一個字元#不匹配s當前字元(p[j-1]!=s[i] && p[j-1]!='.')
- 其他則不匹配:=false
5. base case:
- dp[i][0]=false:任意字串s,匹配空串,除非空串自己,其他都為false。
- dp[0][0]=true
- dp[0][2j]=true:當dp[0][2j-1]==true && p[2j]=='*'
- "#*#*#*...#*"只有這種情況能匹配任意空串字串s。
程式碼參考:
1 class Solution { 2 public: 3 //dp[i][j]:s[0~i],p[0~j] are matched? 4 //case_1:s[i]==p[j] or p[j]=='.': dp[i-1][j-1] 5 //case_2:p[j]=='*': 6 // case_2_1, s[i]!=p[j-1]->match 0 time: dp[i][j-2] 7 // #####a(i) 8 // ^ 9 // ####b*(j) 10 // ^ 11 // case_2_2, s[i]==p[j-1] or p[j]=='.' -> 12 // #####a(i) or #####a(i) 13 // ####.*(j) ####a*(j) 14 // match 0 time: dp[i][j-2] 15 // or match 1 time: dp[i-1][j-2] 16 // #####a(i) 17 // ^ 18 // ####a*(j) 19 // ^ 20 // or match >1 times: dp[i-1][j] 21 // #####a(i) 22 // ^ 23 // ####a*(j) 24 // ^ 25 //base case: 26 //dp[i][0]=false 27 //dp[0][j]=p[j]=='*'&&dp[0][j-2]->"#*#*#*..." 28 //dp[0][0]=true 29 bool isMatch(string s, string p) { 30 int n=s.length(), m=p.length(); 31 vector<vector<bool>> dp(n+1, vector<bool>(m+1, false)); 32 dp[0][0]=true; 33 for(int j=2; j<=m; j+=2) { 34 if(p[j-1]=='*' && dp[0][j-2]) { 35 dp[0][j] = true; 36 } 37 } 38 for(int i=1; i<=n; i++) { 39 for(int j=1; j<=m; j++) { 40 if(s[i-1] == p[j-1] || p[j-1] == '.') { 41 dp[i][j] = dp[i-1][j-1]; 42 } else if(p[j-1] == '*') { 43 if(s[i-1] != p[j-2] && p[j-2] != '.') { 44 dp[i][j] = dp[i][j-2]; 45 } else { 46 dp[i][j] = dp[i][j-2] || dp[i-1][j-2] || dp[i-1][j]; 47 } 48 } 49 } 50 } 51 return dp[n][m]; 52 } 53 };