1. 程式人生 > >【LeetCode】10. Regular Expression Matching(C++)

【LeetCode】10. Regular Expression Matching(C++)

地址:https://leetcode.com/problems/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 = “cab”
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 = “misisp*.”
Output: false

理解:

字串的正則匹配,*可以匹配0個或多個字元,.可以匹配一個任意字元
思路是從頭開始匹配,先看s[i]p[j]是不是相同,或者p[j]=='.',如果是,當前位置是匹配的。
‘*’如果有,必然出現在j+1的位置。

  • 如果p[j+1]=='*',需要判斷兩種情況。
    • s[i]開始的字串和p[j+2]開始的字串是否匹配,即*匹配了0個字元;
    • 或者s[i]p[j]是否相同,且s[i+1]p[j]是否相同,即*匹配了一個字元。
  • 否則,判斷s[i]p[j]是否相同,且s[i+1]p[j+1]是否相同。

實現1:

最簡單的遞迴實現,由於不停的複製string,效率很低

class Solution {
public:
	bool isMatch(string s, string p) {
		if (p.empty()) return s.empty();
		bool first_match = (!s.empty() && (p.at(0) == s.at(0) || p.at(0) == '.'));
		if (p.length() >= 2 && p.at(1) == '*') {
			return isMatch(s, p.substr(2)) || (first_match && isMatch(s.substr(1), p));
		}
		else
			return first_match&& isMatch(s.substr(1), p.substr(1));
	}
};

實現2:

使用下標,代替了字串複製

enum class Match {
	FALSE, TRUE, UNKNOWN
};

class Solution {
	vector<vector<Match>> result;
public:
	bool isMatch(string s, string p) {
		result = vector<vector<Match>>(s.length() + 1, vector<Match>(p.length() + 1, Match::UNKNOWN));
		return dp(0, 0, s, p);
	}

	bool dp(int i, int j, const string& s, const string& p) {
		if (result[i][j] != Match::UNKNOWN)
			return result[i][j] == Match::TRUE;
		bool res;
		if (j == p.length())
			res = (i == s.length());
		else {
			bool first_match = i < s.length() && (s.at(i) == p.at(j) || p.at(j) == '.');
			if (j + 1 < p.length()&&p.at(j+1)=='*') {
				res = dp(i, j + 2, s, p) || (first_match&&dp(i + 1, j, s, p));
			}
			else {
				res = first_match&&dp(i + 1, j + 1, s, p);
			}
		}
		result[i][j] = res ? Match::TRUE : Match::FALSE;
		return res;	
	}
};

實現3:

上面的實現2,還是使用遞迴的方式,然而前面的位置是與後面的位置有關的,因此可以從後向前,記錄後面的判斷結果,避免遞迴呼叫

class Solution {
	vector<vector<bool>> dp;
public:
	bool isMatch(string s, string p) {
		dp = vector<vector<bool>>(s.length() + 1, vector<bool>(p.length() + 1, false));
		dp[s.length()][p.length()] = true;
		for (int i = s.length(); i >= 0; --i)
			for (int j = p.length() - 1; j >= 0; --j) {
				bool first_match = i < s.length() && (s.at(i) == p.at(j) || p.at(j) == '.');
				if (j + 1 < p.length() && p.at(j + 1) == '*') {
					dp[i][j] = dp[i][j + 2] || (first_match &&dp[i + 1][j]);
				}
				else {
					dp[i][j] = (first_match && dp[i + 1][j + 1]);
				}
			}
		return dp[0][0];
	}
};