LeetCode第10题(正则表达式匹配)+一点dp思想概述

本文介绍了LeetCode第10题的解决方法,探讨了如何利用动态规划(DP)思想解决正则表达式匹配问题。讨论了如何根据子串匹配情况推导出更大串的匹配状态,分析了状态存储、状态转换和不同情况下的状态方程,同时强调了数组初始化和特殊情况的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天刚好把第100题给刷完,第10题,在最开始刚刷题的时候,说实话是真的难,当时对dp只是粗略的了解,所以写了很多方法,都是时间复杂度达不到,但又不想百度别人的答案,所以就一直放到现在了。。。原题如下:

给定一个字符串 (s) 和一个字符模式 §。实现支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

‘.’ 匹配任意单个字符。
‘*’ 匹配零个或多个前面的元素。
匹配应该覆盖整个字符串 (s) ,而不是部分字符串。

说明:

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

输入:
s = “aa”
p = “a”
输出: false
解释: “a” 无法匹配 “aa” 整个字符串。
示例 2:

输入:
s = “aa”
p = “a*”
输出: true
解释: ‘*’ 代表可匹配零个或多个前面的元素, 即可以匹配 ‘a’ 。因此, 重复 ‘a’ 一次, 字符串可变为 “aa”。
示例 3:

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

输入:
s = “aab”
p = “cab”
输出: true
解释: ‘c’ 可以不被重复, ‘a’ 可以被重复一次。因此可以匹配字符串 “aab”。
示例 5:

输入:
s = “mississippi”
p = “misisp*.”
输出: false

**

首先你需要弄明白,假如给定一个s,和一个p,我们最终是肯定能得到一个bool值来对应他们的正则表达式的匹配情况,那么如果取s的子串 x和 p的一个子串y ;也一定有一个bool值对应x和y的正则表达式的匹配情况!!!(这个是最优子结构,但是子串没有匹配上,不代表s和p就是不能匹配上了(这并不意味着这不满足最优子结构的条件!!!))有些题确实是这样如果子集部分不满足,就可以直接判断出最终答案;
**

那么这时候你需要思考:我能不能利用他们的子串匹配情况,推倒出比子串稍微大一点的串的情况????如果可以(怎么推倒?) 如果不行(是不是应该想其他的方法?),但往往是可以推倒出来后面的情况,但很难找到他们的状态变化方程{可以把每一步抽象成一个 状态,从前一个(也有可能是多个,甚至是全部)状态导出后一个状态需要一个状态变化方程},这导致我们认为 不能走dp路线。。。

**
0)状态的存储:我们刚拿到这题的话其实是很难知道采取何种数据结构 去存 哪些数据!!!如果知道了的话(这题就已经写出来一半了),不知道的话,怎么办?
穷举!
0.如果是一维数组那么我们可以 存 s的前i个对于 于p(整个)的情况
或者存p的前i个 即 s(整个)与p【0】—p【i】的情况(其实这种情况就这题而言!!!!很快就被pass掉)
1.如果是二维数组(ps:我们至少可以存 3个数据 i ,j,dp【i】【j】){多半这种情况 i 和 j都是对于于不同 的数据 比如 i对于 s ;j对应于p;但是,并不绝对!!!!}
3.其他数据结构,我甚至还自己写过结构体,用过3维数组,,有些题还可能用一个或者几个 基础变量(int…),采取何种数据结构,取决于我们要存的状态(或者也可以说是题的性质);
1)状态的转换(状态方程):(假设你已经决定用dp【i】【j】来存储状态 i 对应于s的0----i号,j对应于p的0----j号,dp【i】【j】表示正则表达式匹配情况)
那么当前状态dp【i】【j】我怎么获得?
dp【i-1】【j-1】,dp【i-1】【j】,dp【i】【j-1】吗…用哪个???或者说用哪些个!!!!或者说是用之前的全部??????(ps:反正是不可能用到 dp【i+1】【j】,dp【i】【j+1】这样的,这叫无后效性!)
就这题而言dp【i】【j】是分情况的:
0.p【j】!=‘.’ &&p【j】!=‘星号’(ps:编辑器非常不好用);这种情况最简单,如果 s【i】==p【j】dp【i】【j】=dp【i-1】【j-1】;否则为0
1.p【j】=‘.’,这种情况和 0.情况差不多,只不过这时候 不用去判断 s【i】和p【j】了
2.p【j】=‘**’,这种情况稍微复杂点又分为:
p【j-1】=‘.’和p【j-1】=‘正常字符’(这个时候p【j-1】为 是没有意义的)
假设p【j-1】为正常字符 t;那么 t
可以是>=0个 如果是0个 dp【i】【j】=dp【i】【j-2】(如果j-2不越界的话) 如果是1个 dp【i】【j】=dp【i】【j-1】,如果是2个那么dp【i】【j】=dp【i-1】【j-1】(这个前提是s【i】也要是t)
假设p【j-1】为‘.’;那么除了类似于(这时候不用要求这个‘.’等于s【i】了)上述正常字符情况外,还有一个很bug的情况,那就是 只要dp【0----**i】*【j-2】有一个是1 那么dp【i】【j】就为1,因为 这时候 p【j-1】和p【j】他们可以与任何字符串匹配

**
其他还需要注意数组的初始化!!!!!还有s.size()为0的情况
**

代码如下:

	vector<vector<int>> get_vector(int a,int b){
		vector<vector<int>>m(a);
		for (int i = 0; i <= m.size() - 1; i++)m[i].resize(b);
		for (int i = 0; i <= m.size() - 1; i++)
		  for (int j = 0; j <= m[i].size() - 1; j++)
			   m[i][j] = 0;
		  return m;
	}
	int check(string s){
		int i = s.size() - 1;
		while (i >= 0){
			if (s[i] == '*') { s.erase(i - 1, 2); i -= 2; }
			else i--;
		}
		if (s.size() == 0)return 1;
		else return 0;
	}
	void chushihua_vector(string s, string p, vector<vector<int>>& m){
			if (p[0] == '.' || p[0] == s[0]) m[0][0] = 1;
			for (int j = 1; j <= p.size() - 1; j++)
			{
				if (p[j] != '*'){
					if (p[j] == s[0] || p[j] == '.'){
						if (check(p.substr(0, j)))m[0][j] = 1;
					}
				}
				else {
					if (j == 1)m[0][j] = m[0][j - 1];
					else m[0][j] = m[0][j - 2] || m[0][j - 1];
				}
			}
	}
	bool if_s_size_eq_zero(string s, string p){
		if (p.size() == 0) return true;
		else {
			string tmp = p;
			for (int i = tmp.size() - 1; i >= 0; i--){
				if (tmp[i] == '*'){
					tmp.erase(i - 1, 2); i--;
				}
				else return 0;
			}
			if (tmp.size() == 0)return true;
			else return false;
		}
	}
	bool isMatch(string s, string p) {
		if (s.size() == 0) return if_s_size_eq_zero(s, p);
		if (s.size() != 0 && p.size() == 0)return false;
		vector<vector<int>>m = get_vector(s.size(), p.size());
		chushihua_vector(s, p, m);
		for (int i = 1; i <= m.size() - 1; i++){
			for (int j = 1; j <= m[i].size() - 1; j++){
				if (p[j] == '*'){
					if (p[j - 1] == '.'){ 
						int x=m[i - 1][j];
						int y = m[i][j-1];
						int z = 0; if (j - 2 >= 0) z = m[i - 1][j - 2];
						m[i][j] = x || y || z;
						if (j - 2 >= 0){
							for (int k = 0; k <= i; k++)
							{
								if (m[k][j - 2] == 1) { m[i][j] = 1; break; }
							}
						}
					}
					else{
						int x = m[i][j - 1];
						int y = m[i - 1][j - 1]; int t = 0;
						int z = 0;
						if (j - 2 >= 0) z = m[i][j - 2];
						if (s[i] == p[j - 1] || p[j - 1] == '.') t = 1;
						y = y&t;
						m[i][j] = (x || y || z);
					}
				}
				else if (p[j] == '.'){
					m[i][j] = m[i - 1][j - 1];
				}
				else{
					if (p[j] == s[i])m[i][j] = m[i-1][j-1];
					else m[i][j] = 0;
				}
			}
		}
		return m[s.size() - 1][p.size() - 1];
	}

**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值