关闭

Leetcode : Regular Expression Matching

标签: 字符串匹配算法动态规划递归leetcode
416人阅读 评论(0) 收藏 举报
分类:

对于leetcode上这个题目,我用了不少时间来消化。
题目大意如下:


实现两个字符串s,t的匹配,其中t字符串中的
‘.’ 能匹配任何一个字符.
‘*’ 能充当0个或者多个前面一个字符.
匹配结果要覆盖整个字符串

几个例子:
isMatch(“aa”,”a”) → false
isMatch(“aa”,”aa”) → true
isMatch(“aaa”,”aa”) → false
isMatch(“aa”, “a*”) → true
isMatch(“aa”, “.*”) → true
isMatch(“ab”, “.*”) → true
isMatch(“aab”, “c*a*b”) → true

比如说最后这个例子c*a*b 能够和aab匹配 就说明第一个* 充当了0个c,第二个 *充当了两个a。


该题有两种较为普遍的解法:递归和动规。以下我就两种方法进行简要的分析。

递归法

该题的一个难点就是当t字符串中出现了符号“ * ”时该如何处理,有时候一下子并不能够清晰的把代码给写出来。我们可以直观地想,出现“ * ”时,该“ * ”可以充当0个,1个,2个…前面的字符,充当的过程还需要满足条件 (s[i]==p[0]) 或者(p[i]==’.’ && s[i]!=’\0’)。 我们将每种可能都与s串进行一遍比较,若有一种可以匹配成功,即是匹配成功。

若t字符串中的当前匹配符号不是符号“ * ”就好办多了,我们直接对 s[i]==p[0] 或者 p[0]==’.’ 进行判断,若匹配成功,那么就可以进行下一步匹配。

参考代码如下:(参考别人)

//递归方法
bool isMatch(string s, string p) 
{
        if ( p.empty() ) return s.empty();
        // p[1]不是*
        if ( p[1]!='*' )
        {
            return ( s[0]==p[0] || (p[0]=='.' && !s.empty()) ) && isMatch(s.substr(1), p.substr(1));
        }
        // p[1]是*
        int i = 0;
        for ( ; s[i]==p[0] || (p[0]=='.' && i<s.size()); ++i)
        {// * 可以充当0个,1个,2个...p[0] 但必须满足匹配条件
            if ( isMatch(s.substr(i), p.substr(2)) ) return true; 
        }
        // p[1] 是 * 但是 p[0] != s[i]
        return isMatch(s.substr(i), p.substr(2));
}

动态规划法

若是对动态规划较为熟悉的人可能对于该方法可能更顺手些。

我们提供一个二维空间集合f[i][j] 其表示字符串s[0..i-1]能否和t[0…j-1]匹配成功,我们很快可以得到动态规划转移方程

  • 当t[j-1]!=’*’时 f[i][j] = f[i-1][j-1] && ( s[i-1] == t[j-1] )

这个很好理解,s[i-1]和t[j-1]若相同,那么其s[0…i-2] 至 t[0…j-2]仍然能匹配成功时,那么这两段字符串就是匹配成功。

  • 当t[j-1]==’*’时 t[j-1]可以充当0个或者多个t[j-2],只要满足以下两个条件中任何一个,f[i][j] 结果即为true
    • t[j-1]充当0个t[j-2] : f[i][j] = f[i][j-2]
    • t[j-1]充当1个及以上t[j-2] :f[i][j] = (s[i - 1] == p[j - 2] || ‘.’ == p[j - 2]) && f[i - 1][j]

上述动规方程中的 f[i - 1][j] 包含了 t[j-1]充当2个,3个…多个t[j-2] 的所有可能性,并且动规过程中,我们已经计算并保存了结果。

参考代码如下:(参考别人)

bool isMatch(string s, string p) {

 int m = s.size(), n = p.size();
        vector<vector<bool>> f(m + 1, vector<bool>(n + 1, false));

        f[0][0] = true;//空串与空串匹配成功
        for (int i = 1; i <= m; i++)//s串与空串匹配结果为false
            f[i][0] = false;
        // p[0..j - 1] 能与空串匹配 需满足 p[j - 1]=='*' 并且 p[0..j - 3] 能与空串匹配
        for (int j = 1; j <= n; j++)
            f[0][j] = j > 1 && '*' == p[j - 1] && f[0][j - 2];

        for (int i = 1; i <= m; i++)
            for (int j = 1; j <= n; j++)
                if (p[j - 1] != '*')
                    f[i][j] = f[i - 1][j - 1] && (s[i - 1] == p[j - 1] || '.' == p[j - 1]);
                else
                    f[i][j] = f[i][j - 2] || (s[i - 1] == p[j - 2] || '.' == p[j - 2]) && f[i - 1][j];

        return f[m][n];
}

两种方法不同的思想,其中有相通的部分,都能解决问题,记录一下以便以后回顾。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:38504次
    • 积分:868
    • 等级:
    • 排名:千里之外
    • 原创:42篇
    • 转载:1篇
    • 译文:0篇
    • 评论:10条
    最新评论