(双序列型动态规划)lintcode困难154 · 正则表达式匹配

题目

描述
实现支持 ‘.’ 和 ‘’ 的正则表达式匹配。’.’ 匹配任意一个字母。’’ 匹配零个或者多个前面的元素,’’ 前保证是一个非 '’ 元素。匹配应该覆盖整个输入字符串,而不仅仅是一部分。需要实现的函数是:
bool isMatch(string s, string p)

isMatch(“aa”,“a”) → false
isMatch(“aa”,“aa”) → true
isMatch(“aaa”,“aa”) → false
isMatch(“aa”, “a*”) → true
isMatch(“aa”, “.* ") → true
isMatch(“ab”, ".
*”) → true

样例

样例 1:
输入:“aa”,“a”
输出:false
解释:
无法匹配

样例 2:
输入:“aa”,“a*”
输出:true
解释:
‘*’ 可以重复 a

样例 3:
输入:“aab”, “cab”
输出:true
解释:
“c*” 作为一个整体匹配 0 个 ‘c’ 也就是 “”
“a*” 作为一个整体匹配 2 个 ‘a’ 也就是 “aa”
“b” 匹配 “b”
所以 “cab” 可以匹配 “aab”

分析

解读题意,给定两个字符串,s和p,s是正常的字符串,p是带符号的字符串,‘.’可以匹配任意的一个字符,比如“a”和‘.’是匹配的,和前置的元素可以匹配0个或多个字符比如“aaaa”和“a ”是匹配的,“aaaa”和“. *”是匹配的。

这道给定了两个序列,所以依然是一道双序列的动态规划,被列为困难题,无非是多了很多匹配的条件,情况变得更多了,依然分析最后一步,然后分析转移方程

最后一步

情况一
A:abc…a
B:dads…a
先看最后一个字符不是*的情况,当两个序列最后一个字符匹配时,我们只需要比较他们前面的序列是否匹配,如果前面的匹配这两个序列就是匹配的f[i][j]=f[i][j]||f[i-1][j-1]

情况二
A:abc…a
B:abc…b
当最后的字符不相同时那一定是不匹配的,我们不需要任何操作,只需要初始化的时候将所有状态都设置成false即可

情况三
A:abc…a
B:abs…’.’
当最后一个字符为‘.’的时候也是跟情况一相同的,因为‘.’可以匹配任意一个字符

下面是最后一个字符为‘*’的时候的情况

情况一

A:asfad…a
B:sada…a*
这个时候最后一个字符是匹配的,但是的概念是可以当作0个或者多个,这个时候我们可以选择用这个a 匹配或者不用这个,f[i][j]=f[i][j] || f[i-1][j] (用a*) ||f[i][j-2] (不用a*)

情况二

A: sad…a
B: sad…’.’*
‘.’可以当作任意一个字符,所以这种情况等同于情况一,我们也可以用它,或者不用他

情况三

A:as…a
B: asd…b*
这个时候最后一个字符不匹配,但是整体不一定不匹配,因为也可以当作0个字符,这个时候不用b *前面的字符串如果匹配的话,这两个字符串也是匹配的,f[i][j]=f[i][j] || f[i][j-2]

转移方程

将最后一步的所有情况都总结起来转移方程就出来了

代码部分

初始化

我们要有空串的情况,所以状态的行和列都应该是字符串的长度加一

        int slen=s.size();
        int plen=p.size();
        
        //f[i][j]:p的前j-1项是否跟s的前i-1项匹配 
        vector<vector<bool> > f(slen+1,vector<bool>(plen+1));

考虑p为空但是s不为空时,一定是无法匹配的的,所以值都设置为false,考虑都为空时一定匹配,所以0,0的值时true,考虑p不为空但是s为空时,有可能匹配“ ” “a*”因为*可以认为是0个当前元素,但是要是“ ” “a”就不匹配,所以我们将这种情况直接带入到转移方程中写

完整代码

class Solution {
public:

    bool isMatch(string &s, string &p) {
        int slen=s.size();
        int plen=p.size();
        
        //f[i][j]:p的前j-1项是否跟s的前i-1项匹配 
        vector<vector<bool> > f(slen+1,vector<bool>(plen+1));
        
        for(int i=0;i<=slen;i++)
        {
        	for(int j=0;j<=plen;j++)
        	{
        		if(i==0&&j==0)		//空串匹配空串 
        		{
        			f[i][j]=true;
        			continue;
				}
				
				if(j==0)			//当p为空且s不为空一定不匹配 
				{
					f[i][j]=false;
					continue;
				} 
				
				f[i][j]=false;
				if(p[j-1]!='*')
				{
					if(i-1>=0&&(p[j-1]=='.'||p[j-1]==s[i-1]))
						f[i][j]=f[i][j]||f[i-1][j-1]; 
				}
				else
				{
					if(i-1>=0&&j-2>=0&&(p[j-2]==s[i-1]||p[j-2]=='.'))
						f[i][j]=(f[i][j]||f[i-1][j]||f[i][j-2]);
					else if(j-2>=0)
						f[i][j]=f[i][j]||f[i][j-2];
				}
			}
		}
		
		return f[slen][plen];
    }
};

总结:

一道双序列型动态规划,之所以被列为困难题,难点在于情况要多一些,将每个情况都一 一分析出来,然后写出转移方程,在转换成代码,注意的点是要在条件中加上对数组下标的判断,不然会导致越界访问

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

White boy&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值