zoukankan      html  css  js  c++  java
  • [LeetCode] 10. Regular Expression Matching 正则表达式匹配

    Given an input string (s) and a pattern (p), 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).

    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 precedeng 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

    通配符匹配问题,和44. Wildcard Matching类似。'.'表示1个字符,'*'表示它前面的字符可以有0个,1个或是多个,比如:字符串a*b,可以表示b或是aaab,即a的个数可以是0个,也可以是多个。

    解法1:DP

    解法2: 递归

    Java:

    public boolean isMatch(String s, String p) {
    
        if (s == null || p == null) {
            return false;
        }
        boolean[][] dp = new boolean[s.length()+1][p.length()+1];
        dp[0][0] = true;
        for (int i = 0; i < p.length(); i++) {
            if (p.charAt(i) == '*' && dp[0][i-1]) {
                dp[0][i+1] = true;
            }
        }
        for (int i = 0 ; i < s.length(); i++) {
            for (int j = 0; j < p.length(); j++) {
                if (p.charAt(j) == '.') {
                    dp[i+1][j+1] = dp[i][j];
                }
                if (p.charAt(j) == s.charAt(i)) {
                    dp[i+1][j+1] = dp[i][j];
                }
                if (p.charAt(j) == '*') {
                    if (p.charAt(j-1) != s.charAt(i) && p.charAt(j-1) != '.') {
                        dp[i+1][j+1] = dp[i+1][j-1];
                    } else {
                        dp[i+1][j+1] = (dp[i+1][j] || dp[i][j+1] || dp[i+1][j-1]);
                    }
                }
            }
        }
        return dp[s.length()][p.length()];
    }
    

      

    Java:

    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:

    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;
            } 
        }
    }
    

    Python: 

    # dp with rolling window
    class Solution:
        # @return a boolean
        def isMatch(self, s, p):
            k = 3
            result = [[False for j in xrange(len(p) + 1)] for i in xrange(k)]
    
            result[0][0] = True
            for i in xrange(2, len(p) + 1):
                if p[i-1] == '*':
                    result[0][i] = result[0][i-2]
    
            for i in xrange(1,len(s) + 1):
                if i > 1:
                    result[0][0] = False
                for j in xrange(1, len(p) + 1):
                    if p[j-1] != '*':
                        result[i % k][j] = result[(i-1) % k][j-1] and (s[i-1] == p[j-1] or p[j-1] == '.')
                    else:
                        result[i % k][j] = result[i % k][j-2] or (result[(i-1) % k][j] and (s[i-1] == p[j-2] or p[j-2] == '.'))
    
            return result[len(s) % k][len(p)]
    

    Python:

    # dp
    # Time:  O(m * n)
    # Space: O(m * n)
    class Solution2:
        # @return a boolean
        def isMatch(self, s, p):
            result = [[False for j in xrange(len(p) + 1)] for i in xrange(len(s) + 1)]
    
            result[0][0] = True
            for i in xrange(2, len(p) + 1):
                if p[i-1] == '*':
                    result[0][i] = result[0][i-2]
    
            for i in xrange(1,len(s) + 1):
                for j in xrange(1, len(p) + 1):
                    if p[j-1] != '*':
                        result[i][j] = result[i-1][j-1] and (s[i-1] == p[j-1] or p[j-1] == '.')
                    else:
                        result[i][j] = result[i][j-2] or (result[i-1][j] and (s[i-1] == p[j-2] or p[j-2] == '.'))
    
            return result[len(s)][len(p)]
    

    Python:

    # iteration
    class Solution3:
        # @return a boolean
        def isMatch(self, s, p):
            p_ptr, s_ptr, last_s_ptr, last_p_ptr = 0, 0, -1, -1
            last_ptr = []
            while s_ptr < len(s):
                if p_ptr < len(p) and (p_ptr == len(p) - 1 or p[p_ptr + 1] != '*') and 
                (s_ptr < len(s) and (p[p_ptr] == s[s_ptr] or p[p_ptr] == '.')):
                        s_ptr += 1
                        p_ptr += 1
                elif p_ptr < len(p) - 1 and (p_ptr != len(p) - 1 and p[p_ptr + 1] == '*'):
                    p_ptr += 2
                    last_ptr.append([s_ptr, p_ptr])
                elif  last_ptr:
                    [last_s_ptr, last_p_ptr] = last_ptr.pop()
                    while last_ptr and p[last_p_ptr - 2] != s[last_s_ptr] and p[last_p_ptr - 2] != '.':
                        [last_s_ptr, last_p_ptr] = last_ptr.pop()
    
                    if p[last_p_ptr - 2] == s[last_s_ptr] or p[last_p_ptr - 2] == '.':
                        last_s_ptr += 1
                        s_ptr = last_s_ptr
                        p_ptr = last_p_ptr
                        last_ptr.append([s_ptr, p_ptr])
                    else:
                        return False
                else:
                    return False
    
            while p_ptr < len(p) - 1 and p[p_ptr] == '.' and p[p_ptr + 1] == '*':
                p_ptr += 2
    
            return p_ptr == len(p)
    

    Python:  

    # recursive
    class Solution4:
        # @return a boolean
        def isMatch(self, s, p):
            if not p:
                return not s
    
            if len(p) == 1 or p[1] != '*':
                if len(s) > 0 and (p[0] == s[0] or p[0] == '.'):
                    return self.isMatch(s[1:], p[1:])
                else:
                    return False
            else:
                while len(s) > 0 and (p[0] == s[0] or p[0] == '.'):
                    if self.isMatch(s, p[2:]):
                        return True
                    s = s[1:]
                return self.isMatch(s, p[2:])
    

    Python:

    class Solution(object):
        def isMatch(self, s, p):
            # The DP table and the string s and p use the same indexes i and j, but
            # table[i][j] means the match status between p[:i] and s[:j], i.e.
            # table[0][0] means the match status of two empty strings, and
            # table[1][1] means the match status of p[0] and s[0]. Therefore, when
            # refering to the i-th and the j-th characters of p and s for updating
            # table[i][j], we use p[i - 1] and s[j - 1].
    
            # Initialize the table with False. The first row is satisfied.
            table = [[False] * (len(s) + 1) for _ in range(len(p) + 1)]
    
            # Update the corner case of matching two empty strings.
            table[0][0] = True
    
            # Update the corner case of when s is an empty string but p is not.
            # Since each '*' can eliminate the charter before it, the table is
            # vertically updated by the one before previous. [test_symbol_0]
            for i in range(2, len(p) + 1):
                table[i][0] = table[i - 2][0] and p[i - 1] == '*'
    
            for i in range(1, len(p) + 1):
                for j in range(1, len(s) + 1):
                    if p[i - 1] != "*":
                        # Update the table by referring the diagonal element.
                        table[i][j] = table[i - 1][j - 1] and 
                                      (p[i - 1] == s[j - 1] or p[i - 1] == '.')
                    else:
                        # Eliminations (referring to the vertical element)
                        # Either refer to the one before previous or the previous.
                        # I.e. * eliminate the previous or count the previous.
                        # [test_symbol_1]
                        table[i][j] = table[i - 2][j] or table[i - 1][j]
    
                        # Propagations (referring to the horizontal element)
                        # If p's previous one is equal to the current s, with
                        # helps of *, the status can be propagated from the left.
                        # [test_symbol_2]
                        if p[i - 2] == s[j - 1] or p[i - 2] == '.':
                            table[i][j] |= table[i][j - 1]
    
            return table[-1][-1]
    
    
    class TestSolution(unittest.TestCase):
        def test_none_0(self):
            s = ""
            p = ""
            self.assertTrue(Solution().isMatch(s, p))
    
        def test_none_1(self):
            s = ""
            p = "a"
            self.assertFalse(Solution().isMatch(s, p))
    
        def test_no_symbol_equal(self):
            s = "abcd"
            p = "abcd"
            self.assertTrue(Solution().isMatch(s, p))
    
        def test_no_symbol_not_equal_0(self):
            s = "abcd"
            p = "efgh"
            self.assertFalse(Solution().isMatch(s, p))
    
        def test_no_symbol_not_equal_1(self):
            s = "ab"
            p = "abb"
            self.assertFalse(Solution().isMatch(s, p))
    
        def test_symbol_0(self):
            s = ""
            p = "a*"
            self.assertTrue(Solution().isMatch(s, p))
    
        def test_symbol_1(self):
            s = "a"
            p = "ab*"
            self.assertTrue(Solution().isMatch(s, p))
    
        def test_symbol_2(self):
            # E.g.
            #   s a b b
            # p 1 0 0 0
            # a 0 1 0 0
            # b 0 0 1 0
            # * 0 1 1 1
            s = "abb"
            p = "ab*"
            self.assertTrue(Solution().isMatch(s, p))
    
    
    if __name__ == "__main__":
        unittest.main()  

    C++:  

    class Solution {
    public:
        bool isMatch(string s, string p) {
            if (p.empty())    return s.empty();
            
            if ('*' == p[1])
                // x* matches empty string or at least one character: x* -> xx*
                // *s is to ensure s is non-empty
                return (isMatch(s, p.substr(2)) || !s.empty() && (s[0] == p[0] || '.' == p[0]) && isMatch(s.substr(1), p));
            else
                return !s.empty() && (s[0] == p[0] || '.' == p[0]) && isMatch(s.substr(1), p.substr(1));
        }
    };
    
    class Solution {
    public:
        bool isMatch(string s, string p) {
            /**
             * f[i][j]: if s[0..i-1] matches p[0..j-1]
             * if p[j - 1] != '*'
             *      f[i][j] = f[i - 1][j - 1] && s[i - 1] == p[j - 1]
             * if p[j - 1] == '*', denote p[j - 2] with x
             *      f[i][j] is true iff any of the following is true
             *      1) "x*" repeats 0 time and matches empty: f[i][j - 2]
             *      2) "x*" repeats >= 1 times and matches "x*x": s[i - 1] == x && f[i - 1][j]
             * '.' matches any single character
             */
            int m = s.size(), n = p.size();
            vector<vector<bool>> f(m + 1, vector<bool>(n + 1, false));
            
            f[0][0] = true;
            for (int i = 1; i <= m; i++)
                f[i][0] = false;
            // p[0.., j - 3, j - 2, j - 1] matches empty iff p[j - 1] is '*' and p[0..j - 3] matches empty
            for (int j = 1; j <= n; j++)
                f[0][j] = j > 1 && '*' == p[j - 1] && f[0][j - 2];
            
            for (int i = 1; i <= m; i++)
                for (int j = 1; j <= n; j++)
                    if (p[j - 1] != '*')
                        f[i][j] = f[i - 1][j - 1] && (s[i - 1] == p[j - 1] || '.' == p[j - 1]);
                    else
                        // p[0] cannot be '*' so no need to check "j > 1" here
                        f[i][j] = f[i][j - 2] || (s[i - 1] == p[j - 2] || '.' == p[j - 2]) && f[i - 1][j];
            
            return f[m][n];
        }
    };
    

    C++:

    class Solution {
    public:
        bool isMatch(string s, string p) {
            if (p.empty())    return s.empty();
            
            if ('*' == p[1])
                // x* matches empty string or at least one character: x* -> xx*
                // *s is to ensure s is non-empty
                return (isMatch(s, p.substr(2)) || !s.empty() && (s[0] == p[0] || '.' == p[0]) && isMatch(s.substr(1), p));
            else
                return !s.empty() && (s[0] == p[0] || '.' == p[0]) && isMatch(s.substr(1), p.substr(1));
        }
    };
    
    class Solution {
    public:
        bool isMatch(string s, string p) {
            /**
             * f[i][j]: if s[0..i-1] matches p[0..j-1]
             * if p[j - 1] != '*'
             *      f[i][j] = f[i - 1][j - 1] && s[i - 1] == p[j - 1]
             * if p[j - 1] == '*', denote p[j - 2] with x
             *      f[i][j] is true iff any of the following is true
             *      1) "x*" repeats 0 time and matches empty: f[i][j - 2]
             *      2) "x*" repeats >= 1 times and matches "x*x": s[i - 1] == x && f[i - 1][j]
             * '.' matches any single character
             */
            int m = s.size(), n = p.size();
            vector<vector<bool>> f(m + 1, vector<bool>(n + 1, false));
            
            f[0][0] = true;
            for (int i = 1; i <= m; i++)
                f[i][0] = false;
            // p[0.., j - 3, j - 2, j - 1] matches empty iff p[j - 1] is '*' and p[0..j - 3] matches empty
            for (int j = 1; j <= n; j++)
                f[0][j] = j > 1 && '*' == p[j - 1] && f[0][j - 2];
            
            for (int i = 1; i <= m; i++)
                for (int j = 1; j <= n; j++)
                    if (p[j - 1] != '*')
                        f[i][j] = f[i - 1][j - 1] && (s[i - 1] == p[j - 1] || '.' == p[j - 1]);
                    else
                        // p[0] cannot be '*' so no need to check "j > 1" here
                        f[i][j] = f[i][j - 2] || (s[i - 1] == p[j - 2] || '.' == p[j - 2]) && f[i - 1][j];
            
            return f[m][n];
        }
    };
    

      

      

     

    类似题目:

    [LeetCode] 44. Wildcard Matching 外卡匹配

    All LeetCode Questions List 题目汇总

  • 相关阅读:
    数字类型内置方法
    流程控制之while循环
    流程控制之if判断
    基本运算符
    格式化输出的三种方式
    Python与用户交互
    解压缩
    布尔值(bool)
    django基础 -- 8.cookie 和 session
    为博客园文章添加目录的方法
  • 原文地址:https://www.cnblogs.com/lightwindy/p/8503683.html
Copyright © 2011-2022 走看看