算法设计与分析 第十一周 通配符匹配


1 题目描述

在这里插入图片描述


2 选题原因

选择一道动态规划的题目,题目比较新颖,也想了比较久。动态规划的方法感觉逻辑上简单了不少,但是效率上却有些欠缺。


3 题目分析及算法

3.1 常规分析

首先要思考的是能够分解成什么样的子问题。最基本的思路是匹配不同长度的部分。
按照这样的思路,我们选取C(x, y)表示第一个字符串的前x长度和第二个字符串的前y长度能否匹配,那么他又和前面的字符串有什么关系呢?
一般情况下,我们能够得到的情况就有几种:

  • 1、匹配字符是?
  • 2、匹配字符是*
  • 3、匹配字符是字符且相同
  • 4、匹配字符是字符且不同

依次考虑几种情况,很明显如果是1、2、3,那么肯定是可以匹配的,这时,只要C(x - 1, y - 1)是匹配即可。
4则一定是不匹配的。

3.2 问题所在

但是按照这样分析很快就发现了有问题:如果是1、3种情况,一定是匹配并且会占用一个字符的。但是情况2就复杂得多。
我们试想:aa*的匹配情况。正常情况下,是可以匹配的。但是如果我们单纯的使用C(x - 1, y - 1)来判断就会出错。因为毫无疑问C(1, 0)是错误的。
为了避免这样的情况,我们可以设想,哪些情况下能够保证*匹配一定是正确的。

  • 1、毫无疑问C(x - 1, y - 1) = true时,一定是成立的。
  • 2、C(x, y - 1) = true时也是成立的。此时,*将匹配一个空字符。
  • 3、C(x - 1, y) = true时也是成立的。此时,*匹配的串将再加入x位置的字符。

因此,我们可以总结出两条规则:

  • 1、当p[y] = '?' || p[y] == s[x]时,只要C(x - 1, y - 1)能够匹配,就是匹配的。
  • 2、当p[y] = '*'时,只要C(x - 1, y - 1) || C(x - 1, y) || C(x, y - 1)能够匹配,就是匹配的。

3.3 初始化问题

这道题的初始化其实是很有意思的。首先我们思考,p[0]能够和谁匹配,答案只能和``字符匹配!s中是不可能出现*的!因此,我们实际上需要匹配的只是一行(或一列)。思考,怎么样匹配呢?道理是一样的,首先,第0个字符(空字符)和第0个字符一定是匹配的!之后呢?当p[i] = '*'的时候,那么只要前面是匹配的,就是匹配的。(由于只能参照前面,斜上方和上方是空的)


3.4 算法

取字符串sm,匹配字符串pn.
初始化:
C(0, 0) = true;
FOR i = 1 : m

  • IF p[i - 1] = * THEN C(i, 0) = C(i - 1, 0)
    //动态规划
    FOR i = 1 : m
  • FOR j = 1 : n
    • IF p[j - 1] = ‘?’ || p[j - 1] = s[i - 1] THEN C(i, j) = C(i - 1, j - 1)
    • ELSE IF p[j - 1] = ‘*’ && (C(i - 1, j - 1) || C(i, j - 1) || C(i - 1, j)) THEN C(i, j) = true
  • END FOR

END FOR


4 关键代码

4.1 初始化

        bool matrix[m + 1][n + 1] = {{0}};
        //初始化
        //只需要初始化p[0]一行
        matrix[0][0] = true;
        //如果当前字符不是*,一定是错误的
        for (int i = 1; i <= n; i++) {
            if (p.at(i - 1) == '*') {
                matrix[0][i] = matrix[0][i - 1];
            } else {
                matrix[0][i] = false;
            }
        }

4.2 动态规划

        //动态规划
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (s.at(i - 1) == p.at(j - 1) || p.at(j - 1) == '?') {                                             //当前字符一定额能够匹配
                    matrix[i][j] = matrix[i - 1][j -1];
                } else if (p.at(j - 1) == '*' && (matrix[i - 1][j - 1] || matrix[i][j - 1] || matrix[i - 1][j])) {  //当前字符可能和前面的匹配
                    matrix[i][j] = true;
                }
            }
        }
        return matrix[m][n];

4.3 防止错误输入

我们考虑,可能会出现输入的字符串为空的这种情况,于是就与先把他处理了。
考虑两种情况:

  • 1、s为空:此时如果p也为空,一定匹配;否则只要p中有非*字符,一定错。
  • 2、p为空:此时如果s也为空,一定匹配,否则一定不匹配。
        //判断字符串为空字符串
        if (s.size() == 0) {
            if (p.size() == 0) {            //两个字符串都是空,那么一定相等
                return true;
            } else {                        //否则只要有字符不是*,就一定不等
                for (string::iterator it = p.begin(); it != p.end(); it++) {
                    if (*it != '*') {
                        return false;
                    }
                }
                return true;
            }
        }
        //如果匹配字符串为空串
        if (p.size() == 0) {
            if (s.size() == 0) {            //如果字符串为空,那么一定相等
                return true;
            } else {                        //字符串不为空,一定不等
                return false;
            }
        }

5 运行结果

在这里插入图片描述


6 源代码

class Solution {
public:
    bool isMatch(string s, string p) {
        //判断字符串为空字符串
        if (s.size() == 0) {
            if (p.size() == 0) {            //两个字符串都是空,那么一定相等
                return true;
            } else {                        //否则只要有字符不是*,就一定不等
                for (string::iterator it = p.begin(); it != p.end(); it++) {
                    if (*it != '*') {
                        return false;
                    }
                }
                return true;
            }
        }
        //如果匹配字符串为空串
        if (p.size() == 0) {
            if (s.size() == 0) {            //如果字符串为空,那么一定相等
                return true;
            } else {                        //字符串不为空,一定不等
                return false;
            }
        }
        int m = s.size();
        int n = p.size();
        bool matrix[m + 1][n + 1] = {{0}};
        //初始化
        //只需要初始化p[0]一行
        matrix[0][0] = true;
        //如果当前字符不是*,一定是错误的
        for (int i = 1; i <= n; i++) {
            if (p.at(i - 1) == '*') {
                matrix[0][i] = matrix[0][i - 1];
            } else {
                matrix[0][i] = false;
            }
        }
        //动态规划
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (s.at(i - 1) == p.at(j - 1) || p.at(j - 1) == '?') {                                             //当前字符一定额能够匹配
                    matrix[i][j] = matrix[i - 1][j -1];
                } else if (p.at(j - 1) == '*' && (matrix[i - 1][j - 1] || matrix[i][j - 1] || matrix[i - 1][j])) {  //当前字符可能和前面的匹配
                    matrix[i][j] = true;
                }
            }
        }
        return matrix[m][n];
        return matrix[m][n];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值