通配符匹配问题: https://leetcode.com/problems/wildcard-matching/
Given an input string (s) and a pattern (p), implement wildcard pattern matching with support for ‘?’ and ‘*’.
‘?’ Matches any single character. ‘*’ Matches any sequence of characters (including the empty sequence).
The matching should cover the entire input string (not partial).
Note:
s could be empty and contains only lowercase letters a-z. p could be empty and contains only lowercase letters a-z, and characters like ? or *.
待匹配字串 s
中只有字母,模式串中 p
只含有字母和 ?
、*
,?
匹配任意单个字符,*
匹配任意多个字符(包括空字串),判断字串是否完全匹配。
使用递归算法会超时。
动态规划
设 $f(i, j)$ 表示 s
前 $i$ 个字符与 p
前 $j$ 个字符是否匹配,当 $i \le 1$ 时不难写出递推公式:
$$f(i, j) = \begin{cases} false & \text{s[i - 1] 与 p[j - 1] 不匹配} \\ f(i - 1, j - 1) & \text{s[i - 1] 与 p[j - 1] 匹配} \\ f(i, j - 1) 或 f(i - 1, j) & \text{p[i - 1] 为 *} \end{cases}$$
对边界情况提前做特殊处理即可。C 语言实现:
时间复杂度为 $O(mn)$。
贪心法
递归版本实现可能出现以下逻辑:
if (p[j] == '*')
return match(i, j + 1) || match(i + 1, j);
这里 match(i, j)
表示从 s[i]
、p[j]
开始是否匹配。
可见,每当遇到一个 *
,都会产生新的分支。
而贪心法只对模式串上一个(已扫描部分的最后一个)*
进行继续搜索,只需记录上一个 *
指针即可。
C 语言实现:
这样,分支明显少于递归版本。
以下是不严格说明:
i
[match][*]abc[*][to match]
[match] * abc * [to match]
第一行为 s
,第二行为 p
。[match]
表示已匹配子串,[to match]
表示待匹配子串。s
中的 [*]
与 p
中的 *
匹配。abc
是一段匹配的字符串(可能含有 ?
)。
设 s
中 c
的下标为 i
。如果回溯第一个 *
,即让它匹配更长的子串,则新匹配(如果有的话)的 c
位置一定在 i
之后,它可能在 [*]
中,也可能在 [to match]
中,但无论如何,这些情况都被“只回溯第二个 *
”包括在内了。因此,只回溯最后一个 *
就可以了。
在最坏情况下,这一算法的时间复杂度也为 $O(mn)$。
实现源码
https://github.com/qianbinbin/leetcode