华为机试—字符串通配符

题目

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

  • ‘*’ 符号代表匹配 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 <bits/stdc++.h>
using namespace std;

// 判断 s 是否能匹配 p
bool isMatch(const string& s, const string& p) {
    int m = s.length();
    int n = p.length();
    vector<vector<bool>> dp(m + 1, vector<bool>(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 <ctype.h>
int isalnum(int c);

C++ 中的 isalnum 函数

#include <cctype>
int isalnum(int c);

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

    ### 华为OD中的增强版 `strstr` 函数 #### 题目描述 题目要求实现一个增强版本的 `strstr` 函数,其功能是在源字符串中查找第一个匹配的目标字符串,并返回目标字符串首次出现位置相对于源字符串起始位置的偏移量。如果未找到,则返回 `-1`。此函数支持带有通配符模式的模糊查询。 #### 解题思路 为了处理带通配符的模糊查询,在遍历过程中需考虑多种情况: - 当前字符完全匹配; - 使用通配符代替任意单个字符; - 处理连续多个通配符的情况; 对于每种编程语言的具体实现方式有所不同,下面分别给出 C++ 和 Python 的解决方案[^1]。 #### C++ 实现方案 ```cpp #include <iostream> #include <string> using namespace std; int enhancedStrstr(const string& haystack, const string& needle) { int m = haystack.size(), n = needle.size(); for (int i = 0; i <= m - n; ++i) { bool match = true; for (int j = 0; j < n && match; ++j) { if (!(haystack[i + j] == '?' || needle[j] == '?' || haystack[i + j] == needle[j])) { match = false; } } if (match) return i; } return -1; } // 主程序用于读取输入并调用上述方法打印结果 int main() { string s, p; cin >> s >> p; cout << enhancedStrstr(s, p); } ``` #### Python 实现方案 ```python def enhanced_strstr(haystack: str, needle: str) -> int: m, n = len(haystack), len(needle) for i in range(m - n + 1): matched = True for j in range(n): if not any([ haystack[i+j] == ch or needle[j] == '?' for ch in [haystack[i+j], '?'] ]): matched = False break if matched: return i return -1 if __name__ == "__main__": source_string = input().strip() pattern_string = input().strip() result = enhanced_strstr(source_string, pattern_string) print(result) ``` 在实际考试环境中需要注意的是,华为 OD 采用 ACM 模式进行考核,因此考生不仅需要完成核心算法逻辑的设计与编码工作,还需要自行负责数据的输入/输出操作部分[^3]。
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    打赏作者

    rigidwill666

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

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

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

    打赏作者

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

    抵扣说明:

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

    余额充值