请实现一个函数用来匹配包含’. ‘和’*‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但与"aa.a"和"ab*a"均不匹配。
示例 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
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母以及字符 . 和 *,无连续的 '*'。
解题思路
- 若是正则表达式字符串中存在唯一的与需要匹配的字符串中不同的字符,那么两者肯定不匹配。
- 例如: s = " a a a a a a a a a a a a a b " , p = " a ∗ a ∗ a ∗ a ∗ a ∗ a ∗ a ∗ a ∗ a ∗ a ∗ c " s="aaaaaaaaaaaaab",p = "a^*a^*a^*a^*a^*a^*a^*a^*a^*a^*c" s="aaaaaaaaaaaaab",p="a∗a∗a∗a∗a∗a∗a∗a∗a∗a∗c"时
- 可以在一开始利用 m a p map map容器去除这个干扰项
- 检查结束后便可利用递归来寻找匹配的正则表达式
代码展示
class Solution {
public:
bool isMatch(string s, string p) {
map<char,int>mp;
for(int i=0;i<s.length();i++)
{
if(!mp.count(s[i]))
mp[s[i]]=1;
}
for(int j=0;j<p.length();j++)
{
if(p[j+1]!='*'&&p[j]!='.'&&p[j]!='*'&&!mp.count(p[j]))
return false;
}
return MatchCore(s,p);
}
bool MatchCore(string s,string p)
{
if(s.empty()&&p.empty())return true;
if(!s.empty()&&p.empty())return false;
if(p[1]=='*')
{
if(s[0]==p[0] || (p[0]=='.'&&!s.empty()))
return MatchCore(s.substr(1),p.substr(2))||MatchCore(s.substr(1),p)||MatchCore(s,p.substr(2)) ;
else
return MatchCore(s,p.substr(2)) ;
}
if(s[0]==p[0] ||(p[0]=='.'&&!s.empty()))
return MatchCore(s.substr(1),p.substr(1));
return false;
}
};
大神思路dp
假设主串为 A A A,模式串为 B B B 从最后一步出发,需要关注最后进来的字符。假设 A A A 的长度为 n n n , B B B 的长度为 m m m ,关注正则表达式 B B B 的最后一个字符是谁,它有三种可能,正常字符、 ∗ * ∗ 和 . . .(点)。
那针对这三种情况讨论即可,如下:
- 如果 B B B 的最后一个字符是正常字符,那就是看 A [ n − 1 ] A[n-1] A[n−1] 是否等于 B [ m − 1 ] B[m−1] B[m−1],相等则看 A 0.. n − 2 A_{0..n-2} A0..n−2 与 B 0.. m − 2 B_{0..m-2} B0..m−2,不等则是不能匹配。
- 如果 B B B 的最后一个字符是 . . .,它能匹配任意字符,直接看 A 0.. n − 2 A_{0..n-2} A0..n−2 与 B 0.. m − 2 B_{0..m-2} B0..m−2
- 如果
B
B
B 的最后一个字符是
∗
*
∗它代表
B
[
m
−
2
]
=
c
B[m-2]=c
B[m−2]=c 可以重复0次或多次,它们是一个整体
c
∗
c^*
c∗
- 情况一: A [ n − 1 ] A[n-1] A[n−1] 是 0 0 0 个 c c c, B B B 最后两个字符废了,能否匹配取决于 A 0.. n − 1 A_{0..n-1} A0..n−1 和 B 0.. m − 3 B_{0..m-3} B0..m−3 是否匹配
- 情况二: A [ n − 1 ] A[n-1] A[n−1] 是多个 c c c 中的最后一个(这种情况必须 A [ n − 1 ] = c A[n-1]=c A[n−1]=c 或者 c = c= c= ‘.’),所以 A A A 匹配完往前挪一个, B B B 继续匹配,因为可以匹配多个,继续看 A 0.. n − 2 A_{0..n-2} A0..n−2 和 B 0.. m − 1 B_{0..m-1} B0..m−1 是否匹配。
转移方程
f [ i ] [ j ] f[i] [j] f[i][j]代表 A A A 的前 i i i 个和 B B B 的前 j j j 个能否匹配
- 前面两个情况可以合并,如果最后字符相匹配,那么 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] f[i][j] = f[i-1][j-1] f[i][j]=f[i−1][j−1]
- 对于第三种情况,对于
c
∗
c^*
c∗ 分为扩展还是不扩展两种情况
- 不扩展:直接砍掉正则串的后面两个, f [ i ] [ j ] = f [ i ] [ j − 2 ] f[i][j] = f[i][j-2] f[i][j]=f[i][j−2],
- 扩展:判断字符串匹配后,正则串不动,主串前移一个, f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j] = f[i-1][j] f[i][j]=f[i−1][j]
代码展示
class Solution {
public:
bool isMatch(string A, string B) {
int n = A.length();
int m = B.length();
bool f[100][100]={false}; //初始化为false
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
if (j == 0) {
f[i][j] = i == 0;
//两者都为空才为true
} else {
if (B[j - 1] != '*') {
//当前字符不为*
if (i > 0 && (A[i - 1] == B[j - 1] || B[j - 1] == '.')) {
f[i][j] = f[i - 1][j - 1];
}
} else {
//当前字符为*
//直接跳过
if (j >= 2) {
f[i][j] |= f[i][j - 2];
}
//选择扩展
if (i >= 1 && j >= 2 && (A[i - 1] == B[j - 2] || B[j - 2] == '.')) {
f[i][j] |= f[i - 1][j];
}
// 这里的或运算很重要,扩不扩展只要有一种情况可以满足,那么我们就认定当前的f[i][j]是匹配的
}
}
//cout<<f[i][j]<<" ";输入一个图,可以帮助理解
}
//cout<<endl;
}
return f[n][m];
}
};