侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1824 篇文章
  • 累计收到 0 条评论

实现支持通配符的字符串匹配算法

2025-12-12 / 0 评论 / 4 阅读

题目

实现支持通配符的字符串匹配算法

信息

  • 类型:问答
  • 难度:⭐⭐⭐

考点

动态规划, 字符串匹配, 通配符处理

快速回答

该问题可通过动态规划解决,核心思路如下:

  • 定义二维DP数组,dp[i][j]表示模式串前j字符是否匹配文本串前i字符
  • 处理三种通配符:
    • 普通字符:需精确匹配
    • '?':匹配任意单个字符
    • '*':匹配零个或多个任意字符
  • 状态转移方程:
    • 若p[j-1]=='*':dp[i][j] = dp[i][j-1](匹配0次) || dp[i-1][j](匹配多次)
    • 否则:dp[i][j] = dp[i-1][j-1] && (s[i-1]==p[j-1] || p[j-1]=='?')
  • 时间复杂度O(mn),空间复杂度可优化至O(n)
## 解析

问题描述

实现一个支持通配符的字符串匹配函数,其中:

  • '?' 匹配任意单个字符
  • '*' 匹配零个或多个任意字符
  • 函数签名:bool isMatch(string s, string p)
  • 示例:
    • isMatch("adceb", "*a*b") → true
    • isMatch("acdcb", "a*c?b") → false

核心算法:动态规划

DP数组定义

dp[i][j] = true 表示文本串s的前i个字符与模式串p的前j个字符匹配

状态转移方程

1. 初始化:
   dp[0][0] = true  // 空模式匹配空文本
   dp[0][j] = dp[0][j-1] && p[j-1]=='*'  // 模式前缀全为*才匹配空文本

2. 状态转移:
   if p[j-1] == '*':
      dp[i][j] = dp[i][j-1]   // 匹配0个字符
               || dp[i-1][j]  // 匹配当前字符并继续用*匹配
   else:
      dp[i][j] = dp[i-1][j-1] && (s[i-1]==p[j-1] || p[j-1]=='?')

代码实现

bool isMatch(string s, string p) {
    int m = s.size(), n = p.size();
    vector<vector<bool>> dp(m+1, vector<bool>(n+1, false));

    // 初始化
    dp[0][0] = true;
    for (int j = 1; j <= n; j++) {
        dp[0][j] = dp[0][j-1] && p[j-1] == '*';
    }

    // DP计算
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (p[j-1] == '*') {
                dp[i][j] = dp[i][j-1] || dp[i-1][j];
            } else {
                dp[i][j] = dp[i-1][j-1] && (s[i-1] == p[j-1] || p[j-1] == '?');
            }
        }
    }
    return dp[m][n];
}

优化方案

  • 空间优化:使用滚动数组将空间复杂度降至O(n)
    vector<bool> dp(n+1, false);
    // 初始化dp[0..n]
    for (int j = 1; j <= n; j++) dp[j] = dp[j-1] && p[j-1]=='*';
    
    for (int i = 1; i <= m; i++) {
        vector<bool> newDp(n+1, false);
        for (int j = 1; j <= n; j++) {
            if (p[j-1]=='*') newDp[j] = newDp[j-1] || dp[j];
            else newDp[j] = dp[j-1] && (s[i-1]==p[j-1] || p[j-1]=='?');
        }
        dp = newDp;
    }
  • 剪枝优化:记录最后一个'*'位置,避免无效计算

常见错误

  • 未正确处理连续'*':多个'*'等价于一个'*'
  • 边界条件错误:空字符串与全'*'模式的匹配
  • 状态转移遗漏:'*'匹配时忽略dp[i-1][j]分支
  • 空间优化时未保存上一行完整状态

扩展知识

  • NFA/DFA解法:将模式转换为非确定有限自动机
  • 贪心+回溯:记录'*'位置和匹配点,失败时回溯(最坏O(mn))
  • 不同通配符语义:正则表达式中的'+'、'{m,n}'等扩展
  • 实际应用:文件路径匹配(如.gitignore)、搜索引擎关键词匹配