10. Regular Expression Matching
问题:
给定一个字符串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 };