Leetcode经典题型:10 正则表达式匹配

Leetcode经典题型:10 正则表达式匹配

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

‘.’ 匹配任意单个字符
‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

说明:

s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
示例 1:

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

示例 3:

输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。

示例 4:

输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。

示例 5:

输入:
s = "mississippi"
p = "mis*is*p*."
输出: false

思路 动态规划

Leetcode 思路

  • (一)状态 状态的选取最困难

f[i][j]表示s1的前i个字符,和s2的前j个字符,能否匹配 即s[0:i-1]和s2[0:j-1]能否匹配

  • (二)转移方程

1 如果s1的第 i 个字符和s2的第 j 个字符相同,或者s2的第 j 个字符为 “.”

  • f[i][j] = f[i - 1][j - 1]

2 如果s2的第 j 个字符为 *

  • 若s2的第 j 个字符匹配 0 次第 j - 1 个字符
    f[i][j] = f[i][j - 2] 比如(ab, abc*)
  • 若s2的第 j 个字符匹配至少 1 次第 j - 1 个字符,
    f[i][j] = f[i - 1][j] and s1[i] == s2[j - 1] or s[j - 1] == ‘.’

这里注意不是 f[i - 1][j - 1], 举个例子就明白了 (abbb, ab*) f[4][3] = f[3][3]

  • (三)初始化

f[0][i] = f[0][i - 2] && s2[i] == *
即s1的前0个字符和s2的前i个字符能否匹配

  • (四)结果

f[m][n]

实现

# include <iostream>
# include <vector>
# include <algorithm>
using namespace std;

class Solution {
public:
	bool isMatch(string s, string p) {
		int s_len = s.size();
		int p_len = p.size();
		vector<vector<int>> dp(s_len + 1, vector<int>(p_len + 1, 0));
		dp[0][0] = 1;
		for (int j = 2; j <= p_len; j++) {
			dp[0][j] = (dp[0][j - 2] && (p[j - 1] == '*'));

		}

		for (int i = 1; i <= s_len; i++) {
			for (int j = 1; j <= p_len; j++) {
				if (s[i - 1] == p[j - 1] || p[j - 1] == '.') {
					dp[i][j] = dp[i - 1][j - 1];
				}
				else if (p[j - 1] == '*') {
					if (s[i - 1] == p[j - 2] || p[j - 2] == '.')
						dp[i][j] = max(dp[i][j - 2], dp[i - 1][j]);
					else
						dp[i][j] = dp[i][j - 2];
				}
			}
		}

		for (int i = 0; i < s_len+1; i++) {
			for (int j = 0; j < p_len+1; j++) {
				cout << dp[i][j];
			}
			cout << endl;
		}
		return dp[s_len][p_len];
	}
};


int main()
{
	string a = "aaabc";
	string b = "a*bc";
	Solution s;
	
	cout<<s.isMatch(a, b) <<endl;
	system("pause");
	return 0;
}

注释版

class Solution {
public:
	bool isMatch(string s, string p) {
		int s_len = s.size();
		int p_len = p.size();
		vector<vector<int>> dp(s_len + 1, vector<int>(p_len + 1, 0));
		dp[0][0] = 1;  //空串和空串总是相等的
		if (p_len > 1) //p>0的情况下, dp[0][1]总是0 1处无论是什么字符都不相等
			dp[0][1] = 0;
		for (int j = 2; j <= p_len; j++) {
			dp[0][j] = (dp[0][j - 2] && (p[j - 1] == '*'));

		}

		for (int i = 1; i <= s_len; i++) { //i和j都从1开始,因为表示s1的前i个字符
			for (int j = 1; j <= p_len; j++) {
				//一  如果s1的第 i 个字符和s2的第 j 个字符相同,那么是否相等要看dp[i - 1][j - 1]的状态;
				if (s[i - 1] == p[j - 1] || p[j - 1] == '.') {
					dp[i][j] = dp[i - 1][j - 1];
				}
				//二  如果字符不相等,还有一次挽回的机会 == '*'
				else if (p[j - 1] == '*') {
					//二者相等时*前字符出现情况				           aa 0       ba 1      aaa  n
					//a*作为: 空字符, 单字符 a, 多字符 aaa...             aaa*       ba*       a*
					if (s[i - 1] == p[j - 2] || p[j - 2] == '.')
					    //0dp[i][j - 2] or 1dp[i][j - 1] or ndp[i - 1][j]   1次的情况可以和n合并
						dp[i][j] = max(dp[i][j - 2], dp[i - 1][j]);    
					else
						//根据匹配规则, 比较匹配串*的前一个字符与字符串中前一个字符        aa
						//二者不相等时, a*只有作为空字符串时才可能匹配,                  aac*
						//这就是说, 略过前一个字符, *字符对应的状态与字符串中前2个字符的状态一致
						dp[i][j] = dp[i][j - 2];
				}
				//三 如果不相等也不等于'*',两个字符串必然不等,赋0(因为初始化就是0,所以一下语句可有可无)
				else dp[i][j] = 0;
			}
		}

		for (int i = 0; i < s_len+1; i++) {
			for (int j = 0; j < p_len+1; j++) {
				cout << dp[i][j];
			}
			cout << endl;
		}
		return dp[s_len][p_len];
	}
};

1, If p.charAt(j) == s.charAt(i) :  dp[i][j] = dp[i-1][j-1];
2, If p.charAt(j) == '.' : dp[i][j] = dp[i-1][j-1];
3, If p.charAt(j) == '*': 
   here are two sub conditions:
               1   if p.charAt(j-1) != s.charAt(i) : dp[i][j] = dp[i][j-2]  //in this case, a* only counts as empty
               2   if p.charAt(i-1) == s.charAt(i) or p.charAt(i-1) == '.':
                              dp[i][j] = dp[i-1][j]    //in this case, a* counts as multiple a 
                           or dp[i][j] = dp[i][j-1]   // in this case, a* counts as single a
                           or dp[i][j] = dp[i][j-2]   // in this case, a* counts as empty

在这里插入图片描述

图片
在这里插入图片描述

参考

Leetcode 思路
https://leetcode-cn.com/problems/regular-expression-matching/solution/dong-tai-gui-hua-ji-hui-su-by-song-19/
https://leetcode-cn.com/problems/regular-expression-matching/solution/qing-xi-yi-dong-e-by-_seekdream/
https://leetcode.com/problems/regular-expression-matching/discuss/5651/Easy-DP-Java-Solution-with-detailed-Explanation/?utm_source=LCUS&utm_medium=ip_redirect_q_uns&utm_campaign=transfer2china

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值