给定两个字符串:s(源字符串)和 p(模式字符串)。模式字符串 p 可以包含两种特殊字符:
.:匹配任意单个字符。*:匹配零个或多个前面的那个元素。你的任务是实现一个支持这两种特殊字符的正则表达式匹配函数,以检查源字符串 s 是否与模式 p 匹配。匹配的定义是,可以通过适当地重复 p 中的 * 字符后面的元素(包括不重复),来使 s 完全匹配 p。
示例 1:
输入:s = "aa" p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:s = "aa" p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:
输入:s = "ab" p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
示例 4:
输入:s = "aab" p = "c*a*b"
输出:true
解释:因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入:s = "mississippi" p = "mis*is*p*."
输出:false
* 在模式串中的特殊作用,即它可以表示它前面字符的任意重复次数(包括零次)。. 字符可以表示任何字符,但只能代表一个字符。s,而不是其中的一部分。代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | /**************************************************************** * Description: 动态规划 正则表达式匹配 * Author: Alex Li * Date: 2023-11-20 18:11:47 * LastEditTime: 2023-11-20 23:26:02 ****************************************************************/ #include <iostream> #include <string> #include <vector> using namespace std; bool isMatch(string s, string p) { int m = s.length(), n = p.length(); vector<vector<bool> > dp(m + 1, vector<bool>(n + 1, false)); dp[0][0] = true; for (int i = 1; i <= n; ++i) { if (p[i - 1] == '*') { dp[0][i] = dp[0][i - 2]; } } for (int i = 1; i <= m; ++i) { for (int j = 1; j <= n; ++j) { if (p[j - 1] == '.' || p[j - 1] == s[i - 1]) { dp[i][j] = dp[i - 1][j - 1]; } else if (p[j - 1] == '*') { dp[i][j] = dp[i][j - 2] || (dp[i - 1][j] && (p[j - 2] == '.' || p[j - 2] == s[i - 1])); } } } return dp[m][n]; } int main() { string s = "aab"; string p = "c*a*b"; cout << (isMatch(s, p) ? "Match" : "No Match") << endl; return 0; } |
算法思想:
dpdp 是一个二维布尔数组,其大小为 (m+1) x (n+1),其中 m 和 n 分别是字符串 s 和模式 p 的长度。dp[i][j] 表示 p 的前 j 个字符是否与 s 的前 i 个字符匹配。dp[0][0] = true:当两个字符串都为空时,它们自然是匹配的。*,那么我们检查它之前的两个字符是否匹配(因为 * 可以表示零个或多个前一个字符)。这是通过 dp[0][i] = dp[0][i - 2] 实现的。对于每个字符的组合(即对于 s 中的每个字符和 p 中的每个字符),我们按以下规则填充 dp 表:
p 的当前字符是普通字符或 .,并且它与 s 的当前字符匹配(对于 .,它匹配任何字符),那么 dp[i][j] 的值取决于去掉这两个字符之前字符串的匹配状态,即 dp[i - 1][j - 1]。如果 p 的当前字符是 *,则有两种情况:
dp[i][j] = dp[i][j – 2] || (dp[i – 1][j] && (p[j – 2] == ‘.’ || p[j – 2] == s[i – 1]));
dp[i][j - 2]:* 表示前一个元素出现了零次。
* 及其之前的字符。p 是 "a*b",当我们考虑到 * 时,我们需要检查 "b" 是否匹配,即忽略 "a*"。dp[i][j - 2] 检查当我们从模式中去掉 * 及其之前的字符时,是否匹配。(dp[i - 1][j] && (p[j - 2] == '.' || p[j - 2] == s[i - 1])):* 表示前一个元素出现一次或多次。
dp[i - 1][j]:这表示如果不考虑 s 的当前字符,p 是否与 s 的剩余部分匹配。这相当于检查 * 表示其之前的元素至少出现一次的情况。(p[j - 2] == '.' || p[j - 2] == s[i - 1]):这检查 * 之前的元素是否匹配 s 的当前字符。p[j - 2] 是 * 之前的字符,它必须是 .(匹配任何字符)或与 s[i - 1] 相同。dp[i][j] 的值。如果任一情况为真,dp[i][j] 就为真,这意味着 p 的前 j 个字符与 s 的前 i 个字符匹配。