[LeetCode 10] Regular Expression Matching
Implement regular expression matching with support for '.'
and '*'.
'.' Matches any single character. '*' Matches zero or more of the preceding element. The matching should cover the entire input string (not partial). The function prototype should be: bool isMatch(const char *s, const char *p)
isMatch("aa","a") → false isMatch("aa","aa") → true isMatch("aaa","aa") → false isMatch("aa", "a*") → true isMatch("aa", ".*") → true isMatch("ab", ".*") → true isMatch("aab", "c*a*b") → true
For s[0 ~ i] and p[0 ~ j], we have the following cases.
1. s[i] == p[j] or p[j] == '.' , reduce the problem to s[0 ~ i - 1] and p[0 ~ j - 1];
2. p[j] == '*',
a. s[i] == p[j - 1] or p[j - 1] == '.' : reduce to s[0 ~ i] and p[0 ~ j - 2] for 0 occurence of p[j - 1]; or reduce to s[0 ~ i - 1] and p[0 ~ j] for 1 occurence of p[j - 1].
b. s[i] != p[j - 1] : reduce to s[0 ~ i] and p[0 ~ j - 2] for 0 occurence of p[j - 1].
3. all other cases s and p do not match.
We can solve this problem based the above analysis both recursively and by dynamic programming.
Solution 1. Recursion
1 public class RegularExpressionMatching { 2 public boolean isMatchRecursion(String str, String pattern) { 3 if(str == null || pattern == null) { 4 return false; 5 } 6 String newPattern = pattern.replaceAll("(\\*){2,}", "*"); 7 return matchHelper(str, str.length() - 1, newPattern, newPattern.length() - 1); 8 } 9 private boolean matchHelper(String str, int idx1, String pattern, int idx2) { 10 if(idx1 < 0 && idx2 < 0 || idx1 < 0 && idx2 <= 1 && pattern.charAt(idx2) == '*') { 11 return true; 12 } 13 else if(idx1 < 0 && idx2 >= 0 || idx1 >= 0 && idx2 < 0) { 14 return false; 15 } 16 if(pattern.charAt(idx2) == '.' || str.charAt(idx1) == pattern.charAt(idx2)) { 17 return matchHelper(str, idx1 - 1, pattern, idx2 - 1); 18 } 19 else if(pattern.charAt(idx2) == '*') { 20 if(idx2 > 0 && (pattern.charAt(idx2 - 1) == str.charAt(idx1) || pattern.charAt(idx2 - 1) == '.')) { 21 return matchHelper(str, idx1, pattern, idx2 - 2) || matchHelper(str, idx1 - 1, pattern, idx2); 22 } 23 else { 24 return matchHelper(str, idx1, pattern, idx2 - 2); 25 } 26 } 27 return false; 28 } 29 public static void main(String[] args) { 30 RegularExpressionMatching test = new RegularExpressionMatching(); 31 System.out.println(test.isMatchRecursion("", "")); //true 32 System.out.println(test.isMatchRecursion("", "*")); //true 33 System.out.println(test.isMatchRecursion("aa", "a")); //false 34 System.out.println(test.isMatchRecursion("aa", "aa")); //true 35 System.out.println(test.isMatchRecursion("aaa", "aa")); //false 36 System.out.println(test.isMatchRecursion("aa", "*")); //false 37 System.out.println(test.isMatchRecursion("aa", "**")); //false 38 System.out.println(test.isMatchRecursion("aa", "a*")); //true 39 System.out.println(test.isMatchRecursion("ab", ".*")); //true 40 System.out.println(test.isMatchRecursion("aab", "*a.b")); //true 41 } 42 }
Solution 2. Dynamic Programming
Initialization:
T[0][0] = true; When both s and p are empty string, the match result should return true.
When s is empty, there is also another case of p that matches the empty string. If each letter a-z or . is followed by *, like a*b*c*, then taking 0 occurence of each letter matches empty string.
1 public boolean isMatchDp(String str, String pattern) { 2 if(str == null || pattern == null) { 3 return false; 4 } 5 String newPattern = pattern.replaceAll("(\\*){2,}", "*"); 6 boolean[][] T = new boolean[str.length() + 1][newPattern.length() + 1]; 7 T[0][0] = true; 8 for(int i = 1; i <= newPattern.length(); i++) { 9 if(i == 1 && newPattern.charAt(i - 1) == '*') { 10 T[0][i] = true; 11 } 12 else if(newPattern.charAt(i - 1) == '*') { 13 T[0][i] = T[0][i - 2]; 14 } 15 } 16 for(int i = 1; i <= str.length(); i++) { 17 for(int j = 1; j <= newPattern.length(); j++) { 18 if(str.charAt(i - 1) == newPattern.charAt(j - 1) || newPattern.charAt(j - 1) == '.') { 19 T[i][j] = T[i - 1][j - 1]; 20 } 21 else if(newPattern.charAt(j - 1) == '*') { 22 if(j == 1) { 23 T[i][j] = false; 24 } 25 else { 26 T[i][j] = T[i][j - 2]; 27 if(str.charAt(i - 1) == newPattern.charAt(j - 2) || newPattern.charAt(j - 2) == '.'){ 28 T[i][j] |= T[i - 1][j]; 29 } 30 } 31 } 32 else { 33 T[i][j] = false; 34 } 35 } 36 } 37 return T[str.length()][newPattern.length()]; 38 }
Related Problems