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] }
  • 其他则不匹配:=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 };
原文地址:https://www.cnblogs.com/habibah-chang/p/13647616.html