华为机试—字符串通配符

题目

在计算机中,通配符是一种特殊语法,广泛应用于文件搜索、数据库、正则表达式等领域。让我们来学习通配符的匹配规则:

  • ‘*’ 符号代表匹配 0 个或以上的数字或字母;
  • ‘?’ 符号代表匹配 1 个数字或字母;
  • 小写字母字符代表匹配自身和自身的大写字母形态;
  • 大写字母字符代表匹配自身和自身的小写字母形态;
  • 其他字符代表匹配自身。

现在,对于给定的通配符字符串 s 和目标字符串 p,不考虑大小写,请判断 s 是否可以匹配得到 p。如果可以,输出 true;否则,输出 false。

在本题中,给定的字符串由 ASCII 码在 33 到 126 范围内的可见字符构成。

示例

示例1

输入:z

           zz

输出:false

示例2

输入:Z*

           zz

输出:true

说明:在这个样例中,‘*’ 匹配 ‘z’。注意,由于不区分大小写,‘Z’ 可以匹配 ‘z’。

示例3

输入:**Z

           0QZz

输出:true

说明:在这个样例中,其中一种匹配方法是:第一个 ‘*’ 匹配 "0Q";第二个 ‘*’ 匹配 ‘Z’;第三个 ‘Z’ 匹配 ‘z’。

示例4

输入:??**

           zz

输出:true

说明:‘*’ 可以匹配 0 个字符。

示例5

输入:HH?*

           HH##1

输出:false

说明:可被 ‘*’ 和 ‘?’ 匹配的字符不包含 ‘#’。

分析

动态规划法

具体步骤

1. 定义状态

设 dp[i][j] 表示模式串 s 的前 i 个字符和目标字符串 p 的前 j 个字符是否匹配。dp[i][j] 为 true 表示匹配成功,为 false 表示匹配失败。

2. 初始化

dp[0][0] = true:空字符串和空模式串是匹配的。

对于模式串 s 以 * 开头的情况,由于 * 可以匹配 0 个字符,所以 dp[i][0] 取决于 dp[i - 1][0]。即如果 s 的前 i - 1 个字符组成的模式串能匹配空字符串,那么加上一个 * 后依然能匹配空字符串。

3. 状态转移方程

在填充 dp 数组时,根据 s[i - 1] 的不同情况进行讨论:

  • 当 s[i - 1] 为 * 时
    • * 可以匹配 0 个字符,此时 dp[i][j] 取决于 dp[i - 1][j],即忽略当前的 * 字符,看 s 的前 i - 1 个字符是否能匹配 p 的前 j 个字符。
    • * 也可以匹配 1 个或多个字符,此时 dp[i][j] 取决于 dp[i][j - 1],即让 * 多匹配一个 p[j - 1] 字符,看 s 的前 i 个字符是否能匹配 p 的前 j - 1 个字符。
    • 所以 dp[i][j] = dp[i - 1][j] || dp[i][j - 1]
  • 当 s[i - 1] 为 ? 时
    • ? 只能匹配 1 个数字或字母。因此,只有当 p[j - 1] 是数字或字母时,dp[i][j] 才取决于 dp[i - 1][j - 1],即看 s 的前 i - 1 个字符和 p 的前 j - 1 个字符是否匹配。
  • 当 s[i - 1] 为普通字符时
    • 普通字符不区分大小写进行匹配。将 s[i - 1] 和 p[j - 1] 都转换为小写后进行比较,如果相等且 dp[i - 1][j - 1] 为 true,则 dp[i][j] 为 true

最终结果存储在 dp[m][n] 中,其中 m 是模式串 s 的长度,n 是目标字符串 p 的长度。如果 dp[m][n] 为 true,则表示模式串 s 能匹配目标字符串 p;否则,不能匹配。

时间复杂度:O(m*n),其中 m 是模式串 s 的长度,n 是目标字符串 p 的长度

空间复杂度:O(m*n)

#include 
using namespace std;

// 判断 s 是否能匹配 p
bool isMatch(const string& s, const string& p) {
    int m = s.length();
    int n = p.length();
    vector> dp(m + 1, vector(n + 1, false));
    // 空字符串和空模式串匹配
    dp[0][0] = true;
    // 处理模式串以 * 开头的情况
    for (int i = 1; i <= m; ++i) {
        if (s[i - 1] == '*') {
            dp[i][0] = dp[i - 1][0];
        }
    }
    // 填充 dp 数组
    for (int i = 1; i <= m; ++i) {
        for (int j = 1; j <= n; ++j) {
            if (s[i - 1] == '*') {
                // * 匹配 0 个或多个数字或字母
                dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
            } else if (s[i - 1] == '?') {
                if (isalnum(p[j - 1])) {
                    // ? 匹配 1 个数字或字母
                    dp[i][j] = dp[i - 1][j - 1];
                }
            } else {
                if (tolower(s[i - 1]) == tolower(p[j - 1])) {
                    // 普通字符匹配自身及大小写形式
                    dp[i][j] = dp[i - 1][j - 1];
                }
            }
        }
    }
    return dp[m][n];
}
int main() {
    string s, p;
    cin >> s >> p;
    if (isMatch(s, p)) {
        cout << "true" << endl;
    } else {
        cout << "false" << endl;
    }
    return 0;
}    

知识充电

isalnum() 函数

isalnum 是 C 和 C++ 标准库中的一个函数,主要用于判断一个字符是否为字母(大写或小写)或者数字。

 C 中的 isalnum 函数

#include 
int isalnum(int c);

C++ 中的 isalnum 函数

#include 
int isalnum(int c);

    如果 c 是字母(大写字母 A - Z 或者小写字母 a - z)或者数字(0 - 9),函数返回一个非零值(通常为一个正整数,表示真);如果 c 不是字母或数字,则返回 0(表示假)。​​​​​​​

    你可能感兴趣的:(华为机考,华为,算法,数据结构,动态规划,c++)