动态规划<2>正则表达式匹配(regular-expression-matching)
本节为动态规划第二题:正则表达式匹配(regular-expression-matching),主要包括:问题描述,算法思路,复杂度分析,C++实现。
1. 问题描述
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持.和*的正则表达式匹配。
. 匹配任意单个字符
*匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖整个字符串s的,而不是部分字符串。
说明:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
示例 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 = “cab” 输出: true
解释: 因为 ‘*’ 表示零个或多个,这里 ‘c’ 为 0 个, ‘a’ 被重复一次。因此可以匹配字符串 “aab”。
示例 5: 输入: s = “mississippi” p = “misisp*.” 输出: false
2. 解法
2.1 回溯算法
当模式串的第i个元素的下一个元素是星号时,会有两种情况:
1)i元素在匹配串中出现0次,我们就保持s不变,将p减掉两个元素,调用isMatch。例如:匹配串bc,模式串a*bc。
2)i元素在匹配串出现一次或多次,先比较i元素和匹配串首元素,相等则保持模式串不变,减掉s的首元素,调用isMatch。例如匹配串aabb,模式串abb。
但是会有一种特殊的情况,例如匹配串abb,模式串aabb。这种情况应该是匹配的,但是按照我们设想的两种情况应该是属于第二种情况,所以算法不会判断属于那种情况,而是将两种情况都运行一遍。
该算法C++代码如下:
//1.回溯算法
bool isMatch(string s, string p) {
if (p.empty())
return s.empty();
bool first_match = (!s.empty() && (p.at(0) == s.at(0) || p.at(0) == '.'));
if (p.length() >= 2 && p.at(1) == '*') {
return (isMatch(s, p.substr(2, p.length() - 2)) ||
(first_match && isMatch(s.substr(1, s.length() - 1), p)));
}
else {
return(first_match && isMatch(s.substr(1, s.length() - 1), p.substr(1, p.length() - 1)));
}
}
2.2 动态规划
我们令dp[i][j]表示s的前i个元素是否可以被p的前j个元素所匹配,于是有:
1)dp[0][0]为true,dp[0][1]为false,dp[][0]这一列为false
2)dp数组大小为dp[s.length+1][p.length+1]
C++代码如下:
bool headMatched(string s, string p, int i, int j) {
return s.at(i) == p.at(j) || p.at(j) == '.';
}
bool isMatch(string s, string p) {
vector<vector<bool>> dp(s.length() + 1, vector<bool>(p.length() + 1));
//初始化,false隐式成立
dp[0][0] = true;
//填写第一行也就是dp[0][2]~dp[0][p.length()]
for (int k = 2; k <= p.length(); k++) {
//p字符串的第2个字符是否等于*,此时j元素仍为0个,所以s不变,p减除两个字符
dp[0][k] = p.at(k - 1) == '*' && dp[0][k - 2];
}
//dp数组剩余部分填写
for (int i = 0; i < s.length(); i++)
{
for (int j = 0; j < p.length(); j++)
{
//p的第j个字符是否为*
if (p.at(j) == '*') {
//1.s不变,p移除两个元素
//2.s[i]与p[j-1]相等,p不变,s移除s[i]
dp[i + 1][j + 1] = dp[i + 1][j - 1] ||
(dp[i][j + 1] && headMatched(s, p, i, j - 1));
}
else {
//s[i]和p[j]相等,移除s[i]和p[j]
dp[i + 1][j + 1] = dp[i][j] && headMatched(s, p, i, j);
}
}
}
return dp[s.length()][p.length()];
}
时间复杂度:O(TP);空间复杂度:O(TP)。
深度学习与数学
关注公号,发现更多
长按识别上方二维码关注