10. Regular Expression Matching

recursion

T: O(|s| * |p|), |s| is s state, |p| is p state,

S: O(|s| * |p|),

class Solution {
    public boolean isMatch(String s, String p) {
        if (p.isEmpty()) { // terminal
            return s.isEmpty();
        }
        boolean firstMatch = (!s.isEmpty()) && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.');
        
        
        // Then, we may ignore this part of the pattern, or delete a matching character in the text.
        if (p.length() >= 2 && p.charAt(1) == '*') {
						// 1. x* matches empty string or at least one character: x* -> xx*
            // 2. *s is to ensure s is non-empty
            // 1. ๅฝ“p็š„็ฌฌไบŒไธชๅญ—็ฌฆไธบ*ๅทๆ—ถ๏ผŒ็”ฑไบŽ*ๅทๅ‰้ข็š„ๅญ—็ฌฆ็š„ไธชๆ•ฐๅฏไปฅไปปๆ„๏ผŒๅฏไปฅไธบ0๏ผŒ้‚ฃไนˆๆˆ‘ไปฌๅ…ˆ็”จ้€’ๅฝ’ๆฅ่ฐƒ็”จไธบ0็š„ๆƒ…ๅ†ต๏ผŒๅฐฑๆ˜ฏ็›ดๆŽฅๆŠŠ่ฟ™ไธคไธชๅญ—็ฌฆๅŽปๆŽ‰ๅ†ๆฏ”่พƒ
            // 2. ๆˆ–่€…ๅฝ“sไธไธบ็ฉบ๏ผŒไธ”็ฌฌไธ€ไธชๅญ—็ฌฆๅ’Œp็š„็ฌฌไธ€ไธชๅญ—็ฌฆ็›ธๅŒๆ—ถ๏ผŒๅ†ๅฏนๅŽปๆŽ‰้ฆ–ๅญ—็ฌฆ็š„sๅ’Œp่ฐƒ็”จ้€’ๅฝ’๏ผŒๆณจๆ„pไธ่ƒฝๅŽปๆŽ‰้ฆ–ๅญ—็ฌฆ๏ผŒๅ› ไธบ*ๅทๅ‰้ข็š„ๅญ—็ฌฆๅฏไปฅๆœ‰ๆ— ้™ไธช
            return (isMatch(s, p.substring(2))) || 
                (firstMatch && isMatch(s.substring(1), p));
        } else {
            // ๅฆ‚ๆžœ็ฌฌไบŒไธชๅญ—็ฌฆไธไธบ*ๅท๏ผŒ้‚ฃไนˆๅฐฑ่€่€ๅฎžๅฎž็š„ๆฏ”่พƒ็ฌฌไธ€ไธชๅญ—็ฌฆ๏ผŒ็„ถๅŽๅฏนๅŽ้ข็š„ๅญ—็ฌฆไธฒ่ฐƒ็”จ้€’ๅฝ’
            return firstMatch && isMatch(s.substring(1), p.substring(1));
        }
        
    }
}

/*
key is '*' Matches zero or more of the preceding element.

   "ab", "a.*b"
       |
   (a,a)     &     (b , .*b)
              /                 \ 
    .* matches 0             .* matches > 0 chars
      |                          |
      (b,b) return true first
 */

dp

1. 
dp[i][j]: The matching pattern p[0:j] should cover the entire input string s[0:i].

2. think of these conditions
dp[i-1][j-1]
dp[i][j-1]
dp[i-1][j]


if (isChar(p[j])) {
    dp[i][j] = s[i] == p[j] && dp[i-1][j-1]
}
if (p[j] == '.') {
    dp[i][j] = dp[i-1][j-1]
}

possible1
xxxxxz
yyyyz*

possible2
xxxxxx
yyyyz*  => z* is empty
if (p[j] == '*') {
    possible1 = (p[j-1] == s[i] || p[j-1] == '.') && dp[i-1][j])  // when * means not empty
    possible2 = dp[i][j-2])  // when * means empty, 
    dp[i][j] = possible1 || possible2;
}

3. think of these init value
dp[0][0] = true
dp[0][j] =  (p[j] == *) && dp[0][j-2]
input = ""
so only in p = y*y*y* && y* = ""  can be done, so dp[0][j] = (p[j] == *) && dp[0][j-2]


dp[i][0]
pattern = ""; so all false


time: O(m*n)
space: O(m*n)

time: O(mn)

space: O(mn)

class Solution {
    public boolean isMatch(String s, String p) {
        int m = s.length();
        int n = p.length();
        boolean[][] dp = new boolean[m+1][n+1];
        
        s = "#" + s;
        p = "#" + p;
        
        dp[0][0] = true;
        for (int j = 2; j <= n; j++) {
            dp[0][j] =  (p.charAt(j) == '*') && dp[0][j-2];
        }
        
        for (int i = 1 ; i <= m; i++) {
            for (int j = 1; j <= n; j++) {

                if ((s.charAt(i) == p.charAt(j)) || p.charAt(j) == '.') {
                    dp[i][j] = dp[i-1][j-1];
                }
                
                if (p.charAt(j) == '*') {
                    // when * means not empty
                    boolean possible1 = (p.charAt(j-1) == s.charAt(i) || p.charAt(j-1) == '.') && dp[i-1][j]; 
                        
                    // when * means empty, 
                    boolean possible2 = dp[i][j-2];
                    
                    dp[i][j] = possible1 || possible2;
                }
            }
        }
        return dp[m][n];
    }
}

Last updated