题目分析
题目链接:10. Regular Expression Matching
我们需要回答这么一个问题:模式p
(正则表达式)是否匹配字符串s
。
经过思考,难点主要在于*
究竟应该匹配多少个字符。
因为*
并不是匹配越多字符越好(不能贪婪匹配):
比如模式a*abc
中的a*
只能匹配aaabc
的aa
。如果a*
匹配了aaa
,那么剩余的模式abc
(a*abc
减去开头的a*
)就无法匹配剩余的字符串'bc'。换一种说法,如果a*
匹配3个a,a*a
(4个a)无法匹配aaabc
的任何前缀。
同理,如果匹配的字符少了,也会导致模式无法匹配字符串:
还是上面这个例子,如果a*abc
中的a*
只匹配a
,那么剩余的模式abc
就无法匹配剩余的字符串'aabc'。换一种说法,如果a*
只匹配1个a,a*ab
(aab)无法匹配aaabc
的任何前缀。
动态规划要求:从最简单的子问题出发,利用已知的子问题的答案,得到更复杂的子问题的答案,循环往复,最终得到祖先问题的答案。
假设有字符串s0s1s2...sx
与模式p0p1p2...py
(其中pn代表一个子模式,也就是字符以及它后面的*
):
-
如果py不是*匹配,那么
字符串s0s1s2...sx
匹配模式p0p1p2...py
当且仅当:sx == py指定的字符 &&
s0s1s2...sx-1
匹配p0p1p2...py-1
-
如果py是*匹配,那么
字符串s0s1s2...sx
匹配模式p0p1p2...py
当且仅当:s0s1s2...sx
匹配p0p1p2...py-1
,也就是说去掉py
以后字符串依然与模式匹配。此时py匹配0个字符。或 sx == py指定的字符 &&
s0s1s2...sx-1
匹配p0p1p2...py
,也就是说去掉sx
以后字符串依然与模式匹配。此时py匹配1个或多个字符。
我们用二维数组match
来存储已经解出的子问题答案。match[x][y]
表示s0s1s2...sx-1
是否匹配p
的的p0p1p2...py-1
。特别地,x=0时表示字符串是空串,y=0时表示模式是空模式。
代码实现
class Solution
{
public:
bool isMatch(string s, string p)
{
vector<vector<bool>> match(s.size() + 1, vector<bool>(p.size() + 1, false));
// match[x][y]表示s0s1s2...sx-1是否匹配p的的p0p1p2...py-1。特别地,x=0时表示字符串是空串,y=0时表示模式是空模式。
match[0][0] = true; // 空串与空模式匹配
int j = 1, lastPatternIndex = 0; // lastPatternIndex存储了上一个子模式的下标
char currentPatternChar = 0; // 当前子模式需要匹配的字符
bool isMulti = false; // 当前子模式是不是*匹配
while (j <= p.size())
{
isMulti = false; // isMulti默认为false
// j是当前子模式在p中的下标
// 获取当前子模式的信息
currentPatternChar = p[j - 1];
while (j < p.size() && p[j] == '*')
{ // 将当前子模式的*跳过
isMulti = true;
++j;
}
// i是s当前检测到的字符的下标
for (int i = 0; i <= s.size(); ++i)
{
if (!isMulti)
{
// 非*匹配
// s0s1s2...sx-1匹配p0p1p2...py-1
// && sx == py指定的字符(至少需要一个字符)
match[i][j] = i >= 1 && match[i - 1][lastPatternIndex] && (currentPatternChar == '.' || s[i - 1] == currentPatternChar);
}
else
{
// *匹配
// s0s1s2...sx匹配p0p1p2...py-1
// || (sx == py指定的字符(至少需要一个字符) && s0s1s2...sx-1匹配p0p1p2...py)
match[i][j] = match[i][lastPatternIndex] ||
((currentPatternChar == '.' || s[i - 1] == currentPatternChar) && i >= 1 && match[i - 1][j]);
}
}
lastPatternIndex = j;
++j;
}
return match[s.size()][p.size()];
}
};
时间复杂度
算法由2层扫描组成:外层扫描p的每一个子模式,内层扫描s的每一个字符。
因此,该算法的时间复杂度为O(mn),其中m为p的子模式个数,n为s的字符个数。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。