正则表达式匹配--C++动态规划实现详解

题目描述:

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

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

解答:

分析:根据题目意思 '.' 可以表示任意字符,也就是说'.'可以匹配s中任意字符。'*'取决于前一个字符,所以可以把a*这样的字符看着整体。那么题目要求的意思是。字符串s与p相匹配,也就是将'.'和'*'进行转换后s=p,这里举两个例子:

1.s="aa",p="a*" 这里把a*看着一个整体,它所表达的意思就是多个a,但也可以是0个a也就是空字符,所以把a*看着aa,即可与s匹配。

2.s="aaa",p="a*b*ac*a" 这里把a*看着a,b*看着空字符,c*也看着空字符,就变成了aaa,所以p也可以等于aaa。因此这个题的关键点就在于处理像a*这样的整体,该怎么变他们才能与s匹配。

那么我们可以用动态规划的状态转移思维来想。设状态dp[i][j]为s的前i个字符与p的前j个字符是否相匹配。设初始状态dp[0][0]=1表示前0个,也就是空字符状态下两个字符一定匹配。

现在初始状态知道了,中间状态的递推式该怎么去推,这是一个难点。可以先分情况讨论

第一种情况最简单,当s[j-1]=p[j-1] or p[j-1]='.'时(注:由于 i 和 j 表示的是前 i 或者前 j 个字符,所以第i个字符的下标就是i-1),也就是在第 i 个字符和第 j 个字符相等时。那么dp[i][j]=dp[i-1][j-1]。(在s的第i个字符与p的第j个字符相等时,那么当前字符串是否匹配取决于前i-1个和前j-1个字符串是否匹配)

第二种情况 就是s[j-1]!=p[j-1] 且p[j-1]!='.'时 这个时候就需要判断p[j-1]是否为'*'.如果不是,那就可以直接断定不匹配了。

如果p[j-1]=*。dp[i][j]该怎么往前推算呢。 我举个例子。

s="aaa",p="a*b*aa*"

我们前面说了像 a*这样的看着一个整体,表示n个a。

我们在遇到p[j-1]=*时,就需要判断   if(p[j-2]==s[j-1])  则 说明这个整体可以匹配,但是要把他当作多个来匹配还是只当中一个来匹配,还是直接不匹配。其实这不是一个选择,这三种情况都可以。

如果看着多个来匹配,就有 dp[i][j]=dp[i-1][j]。这里为什么j没有减一。因为看着多个字符后,第j个字符是*。你移动后就不在是*,就不在是多个前面哪一个字符了。

如果看着一个字符来匹配。dp[i][j]=dp[i-1]dp[j-2]。这里j-2 就是*和前面那个字符看成了一个字符来匹配。

如果看着空字符,也就是不匹配。dp[i][j]=dp[i][j-2]。这里不减一是因为不匹配。

这三种情况都可以。所以选择其中一种能够使整个字符串都能够匹配上的方案,如果都匹配不上那就是不匹配了。

 if(juge(s[i-1],p[j-2]))//如果判断出前一个字符匹配,将这三种情况全部判断一遍直到判断出能够全部匹配的状态即dp[i][j]==1。
          {
                   dp[i][j]=dp[i-1][j];
                    if(dp[i][j]==0)
                    {
                        dp[i][j]=dp[i-1][j-2];
                        if(dp[i][j]==0)
                        {
                           dp[i][j]=dp[i][j-2]; 
                        }
                    }
           }

全部代码

class Solution {
public:
   bool juge(char a,char b)//判断两个字符是否匹配
   {
       if(a==b||b=='.')//只要两个字符相等,或者p的字符为.就匹配返回真
       {
           return true;
       }
     return false;
   }
   bool isMatch(string s, string p) {
   int m=s.size();
   int n=p.size();
vector<vector<int>> dp(m+1,vector<int>(n+1));
    dp[0][0]=1;
    for(int i=0;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
             if(i==0)//当s为空的情况下,p不为空,但是有*可以使前一个字符为空。
                {
                    if(p[j-1]=='*')
                     {
                         dp[i][j]=dp[i][j-2];
                    }
                }
           else if(p[j-1]=='*')
            {
                dp[i][j]=dp[i][j-2];//如果*的前一个字符不匹配,j前进两位,*和前一个字符看着空字符
             if(juge(s[i-1],p[j-2]))//如果匹配,判断3种情况
                {
                    dp[i][j]=dp[i-1][j];
                    if(dp[i][j]==0)
                    {
                        dp[i][j]=dp[i-1][j-2];
                        if(dp[i][j]==0)
                        {
                           dp[i][j]=dp[i][j-2]; 
                        }
                    }
                }
            }
             else //最简单的情况
             {
            if(juge(s[i-1],p[j-1]))
            {
                dp[i][j]=dp[i-1][j-1];
            }
             }
        }
    }
   return dp[m][n];
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值