题目
给你一个字符串s
和一个字符规律p
,请你来实现一个支持'.'
和'*'
的正则表达式匹配。
'.'
匹配任意单个字符
'*'
匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖整个字符串s
的,而不是部分字符串。
动态规划方法
建立矩阵matrix记录匹配状态,matrix[i + 1][j + 1]
表示字符规律p
中前i
个字符能否与s
中的前j
个字符相匹配。
这里将带'*'
的字符和'*'
视作一个整体,即在matrix
中的状态行相同。
思路:从p
中第一个匹配字符开始进行匹配,每一个匹配字符都与s
中的所有(存在限制,后续说明)字符尝试匹配一次,并在matrix
中进行记录。因此matrix
的填写方式为:逐行从左至右填写。
注意:
带'*'
的字符数量可以为0,所以存在多种匹配方式,譬如s
为"aaa"
,p
为"a*aaa"
,a*
可以与第一个s
中的第一个a
进行匹配,但紧接着匹配第二个匹配字符(*
后的a
)时,需要退回到s
中的第一个字符匹配,否则后面两个匹配字符无法完全匹配。
而不带'*'
的匹配字符(在此称作独立匹配字符)的任何匹配都是硬匹配的,即后续匹配字符不能退回到独立匹配字符所匹配的字符之前进行匹配,譬如例子中第二个匹配字符与s
中第一个a
匹配后,第三个匹配字符就不能与该字符进行匹配。代码中将用变量mark
标识目前硬匹配的最小索引。
代码:
bool isMatch(string s, string p) {
bool matrix[256][256] = {0}; // 建立矩阵
int flag; // 用于标记p中是否存在某个独立匹配字符没有得到匹配
int mark = 0; // 标记硬匹配的最小索引
for(int i = 0; i < p.length(); i++)
matrix[i][0] = 1; // 矩阵左侧一列置1,便于后续判断是否进行匹配
for(int i = 0; i < p.length() + 1; i++){
flag = 1;
for(int j = mark; j < s.length() + 1; j++){
if(p[i] == '*'){ // 匹配字符为*,直接将前一行的匹配情况抄下来
flag = 0; // 非独立匹配字符,取消标记
for(int k = 1; k < s.length() + 1; k++)
matrix[i + 1][k] = matrix[i][k];
}
else if(i < p.length() && p[i + 1] == '*'){ // 带*的匹配字符
if(flag)
matrix[i + 1][j] = matrix[i][j]; // 解释1
flag = 0; // 非独立匹配字符,取消标记
matrix[i + 1][j + 1] = matrix[i][j + 1]; // 解释2
if(matrix[i][j] == 1 || matrix[i + 1][j] == 1){ // 解释3
if(p[i] == s[j] || p[i] == '.' || matrix[i][j + 1] == 1){
matrix[i + 1][j + 1] = 1;
}
}
}
else if(matrix[i][j] == 1){ // 独立匹配字符的情况
if(p[i] == s[j] || p[i] == '.'){
matrix[i + 1][j + 1] = 1;
if(flag)
mark = j + 1;
flag = 0;
}
}
}
if(flag){
return 0; // 独立匹配字符没有匹配项,匹配失败
}
}
return matrix[p.length()][s.length()]; // 返回最终结果
}
解释1:在flag
为1
时将前一行的匹配状态落到本行。因为带*
的匹配字符的数目可以为0
,这种情况下需要保留上一个匹配字符的匹配状态。
解释2:同样是为了保留上一个匹配字符的匹配状态,但分两部分进行是因为mark
保留的是硬匹配匹配字符所对应的字符的下一个索引,因此解释1的部分是为了保留第一个匹配的状态。而后续的匹配状态由解释2对应的代码保留。可见,带*
的匹配字符的状态为该字符的成功匹配情况和上一个匹配字符的匹配情况的并集。
解释3(动态规划的核心之一):带*
的匹配字符能够进行匹配的两个条件。或是前一个匹配字符与前一个待匹配字符成功匹配,或是自己本身与前一个待匹配字符成功匹配。
解释4(动态规划的核心之二):独立匹配字符能够与当前带匹配字符进行匹配的条件,前一个匹配字符必须与前一个待匹配字符成功匹配。