LeetCode – Regular Expression Matching (Java)

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)

Some examples:
isMatch(“aa”,”a”) return false
isMatch(“aa”,”aa”) return true
isMatch(“aaa”,”aa”) return false
isMatch(“aa”, “a*”) return true
isMatch(“aa”, “.*”) return true
isMatch(“ab”, “.*”) return true
isMatch(“aab”, “c*a*b”) return true

1. Analysis

First of all, this is one of the most difficulty problems. It is hard to think through all different cases. The problem should be simplified to handle 2 basic cases:

  • the second char of pattern is “*”
  • the second char of pattern is not “*”

For the 1st case, if the first char of pattern is not “.”, the first char of pattern and string should be the same. Then continue to match the remaining part.

For the 2nd case, if the first char of pattern is “.” or first char of pattern == the first i char of string, continue to match the remaining part.

2. Java Solution 1 (Short)

The following Java solution is accepted.

public class Solution {
    public boolean isMatch(String s, String p) {
 
        if(p.length() == 0)
            return s.length() == 0;
 
        //p's length 1 is special case    
        if(p.length() == 1 || p.charAt(1) != '*'){
            if(s.length() < 1 || (p.charAt(0) != '.' && s.charAt(0) != p.charAt(0)))
                return false;
            return isMatch(s.substring(1), p.substring(1));    
 
        }else{
            int len = s.length();
 
            int i = -1; 
            while(i<len && (i < 0 || p.charAt(0) == '.' || p.charAt(0) == s.charAt(i))){
                if(isMatch(s.substring(i+1), p.substring(2)))
                    return true;
                i++;
            }
            return false;
        } 
    }
}

3. Java Solution 2 (More Readable)

public boolean isMatch(String s, String p) {
	// base case
	if (p.length() == 0) {
		return s.length() == 0;
	}
 
	// special case
	if (p.length() == 1) {
 
		// if the length of s is 0, return false
		if (s.length() < 1) {
			return false;
		}
 
		//if the first does not match, return false
		else if ((p.charAt(0) != s.charAt(0)) && (p.charAt(0) != '.')) {
			return false;
		}
 
		// otherwise, compare the rest of the string of s and p.
		else {
			return isMatch(s.substring(1), p.substring(1));
		}
	}
 
	// case 1: when the second char of p is not '*'
	if (p.charAt(1) != '*') {
		if (s.length() < 1) {
			return false;
		}
		if ((p.charAt(0) != s.charAt(0)) && (p.charAt(0) != '.')) {
			return false;
		} else {
			return isMatch(s.substring(1), p.substring(1));
		}
	}
 
	// case 2: when the second char of p is '*', complex case.
	else {
		//case 2.1: a char & '*' can stand for 0 element
		if (isMatch(s, p.substring(2))) {
			return true;
		}
 
		//case 2.2: a char & '*' can stand for 1 or more preceding element, 
		//so try every sub string
		int i = 0;
		while (i<s.length() && (s.charAt(i)==p.charAt(0) || p.charAt(0)=='.')){
			if (isMatch(s.substring(i + 1), p.substring(2))) {
				return true;
			}
			i++;
		}
		return false;
	}
}

Java Solution 3 – Using Index

We can also add a helper method to use the index, instead of passing sub-strings recursively.

public boolean isMatch(String s, String p) {
    return helper(s, p, 0, 0);
}
 
private boolean helper(String s, String p, int i, int j) {
    if (i >= s.length() && j >= p.length()) {
        return true;
    }
 
 
    if (j < p.length() - 1 && p.charAt(j + 1) == '*') {
        if (helper(s, p, i, j + 2)) {
            return true;
        }
 
        if (p.charAt(j) == '.') {
            for (int k = i; k < s.length(); k++) {
                if (helper(s, p, k + 1, j + 2)) {
                    return true;
                }
            }
        } else {
            for (int k = i; k < s.length(); k++) {
                if (s.charAt(k) == p.charAt(j)) {
                    if (helper(s, p, k + 1, j + 2)) {
                        return true;
                    }
                } else {
                    break;
                }
            }
        }
    } else if (i < s.length() && j < p.length() && (s.charAt(i) == p.charAt(j) || p.charAt(j) == '.')) {
        return helper(s, p, i + 1, j + 1);
    }
 
    return false;
}

19 thoughts on “LeetCode – Regular Expression Matching (Java)”

  1. Are u kidding me !!! Read question before commenting. Question clearly says
    ” ‘*’ Matches zero or more of the preceding element. ”

    which means any char before * (star) can have zero occurrence too so in this case your case you can ignore “c*”

  2. Passed Leetcode :


    public boolean isMatch(String s, String p) {
    return isMatch(s,p,0,0);
    }
    public boolean isMatch(String s, String p, int i, int j){
    if(i == s.length() && j == p.length()) return true;
    if(i == s.length() && j <= p.length()-2 && p.charAt(j+1) == '*') return isMatch(s,p,i,j+2);
    if(j == p.length() || i == s.length()) return false;

    // If we have a ".*" :
    if(p.charAt(j) == '.' && j+1 < p.length() && p.charAt(j+1) == '*') return isMatch(s,p, i+1,j) || isMatch(s,p,i,j+2);

    // If we have a "*" :
    if(j+1 < p.length() && p.charAt(j+1) == '*') return isMatch(s,p,i, j+2) || (s.charAt(i) == p.charAt(j) && isMatch(s,p,i+1,j));

    // If only '.' :
    if(p.charAt(j) == '.') return isMatch(s,p,i+1,j+1);

    // If only :
    return (s.charAt(i) == p.charAt(j) && isMatch(s,p,i+1,j+1));
    }

  3. This was accepted for me…


    bool isMatch(char *s, char *p) {

    if (*s == '' && *p == '') {
    return true;
    }

    if (*(p+1) == '*') {

    char ch = *p;
    int any = 0;

    if (isMatch(s, p+2)) return true;

    while (*(s+any) != '' &&
    (*(s+any) == ch || ch == '.')) {
    if (isMatch(s+any+1, p+2)) return true;
    any = any + 1;
    }
    return false;
    }

    if (*s == '' || *p == '') {
    return false;
    }

    if (*s == *p) {
    if (isMatch(s+1, p+1)) return true;
    }

    if (*p == '.') {
    if (isMatch(s+1, p+1)) return true;
    }

    return false;
    }

  4. Mine failing for
    “ab”
    “.*”

    Not sure how they match
    if “.*” matches nothing, it must fail
    If “.*” matches 1 “a”, it must still fail

    ???

  5. In regular expressions, does this count as a regular expression?
    p****

    In my mind, the input with string: p and pattern: p**** would return true. but the solution program doesn’t……………

  6. A non-recursive approach. It assumes the existence of an invalid matchingChar which may not be a valid assumption (but could be easily replaced with an optional or null and Character usage).


    boolean isMatch(String str, String pattern) {
    if (str.isEmpty() ^ pattern.isEmpty()) return false;

    boolean isMatchingStar = false;
    char matchingChar = ''; //a non-valid char

    int i = 0;
    int p = 0;
    while (i p && ! isMatchingStar) {
    matchingChar = pattern.charAt(p);
    //may add check for special char for sanity (invalid expression)
    ++p;
    if (pattern.length() > p && pattern.charAt(p) == '*') { //look ahead for *
    isMatchingStar = true;
    ++p;
    }
    } else if (! isMatchingStar) {
    matchingChar = ''; //or some other invalid char
    }
    if (str.charAt(i) == matchingChar || matchingChar == '.') {
    ++i;
    } else {
    if (! isMatchingStar) return false;
    else isMatchingStar = false;
    }
    }

    //we completed the str, need to check if there is still pattern to match
    while (p < pattern.length()) {
    int next = p + 1;
    //if there is a char but no wildcard, pattern not matched
    if (pattern.length() <= next || pattern.charAt(next) != '*')
    return false;
    p = next + 1;
    }
    return true;
    }

    Time complexity O(N), Space O(1)

  7. Hi,
    Shouldn’t the following be true
    String s = “a”;
    String p = “a*”;
    ?
    According to the given code, it will evaluate to false.

  8. “aab” doesn’t match “c*a*cbaab”. Because ‘*’ means the preceding character occurs 0 or more times, here ‘c’ and ‘a’ occurs 0 time and “c*a*cbaab” equals “cbaab”.

  9. When testing the solution I have a problem with this test case:

    assertEquals(true, LeetCode.isMatch(“aab”, “c*a*cbaab”));

    Any ideas on what is the problem?

  10. hey!
    Its a bad habit to use substring function in Java. Since Java 7 strings are not indexed. Complexity of substring function has changed from O(1) to O(n) as now Java copies whole string into new one and returns it instead of moving pointers in the string.

    So the complexity of your code has increased n-times. Sadly test cases at leetcode are quite weak to check complexity issues.

Leave a Comment