请实现一个函数用来匹配包括'.'和'*'的正则表达式。
1.模式中的字符'.'表示任意一个字符
2.模式中的字符'*'表示它前面的字符可以出现任意次(包含0次)。
在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
数据范围:
1.str 只包含从 a-z 的小写字母。
2.pattern 只包含从 a-z 的小写字母以及字符 . 和 *,无连续的 '*'。
3. 0 \le str.length \le 26 \0≤str.length≤26
4. 0 \le pattern.length \le 26 \0≤pattern.length≤26
分析如下:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @param pattern string字符串
* @return bool布尔型
*/
bool match(char* str, char* pattern ) {
if(str == NULL || pattern == NULL){
return false;
}
if(*str == '\0' && *pattern == '\0'){
return true;
}else if(str[0] == 0 && pattern[0] != 0){
if(pattern[1] == '*'){
return match(str, pattern+2);
}else{
return false;
}
}else if(str[0] != 0 && pattern[0] == 0){
return false;
}
if(*pattern == *str){
if(pattern[1] == '*'){
return match(str+1, pattern) || match(str, pattern+2);
}else{
return match(str+1, pattern+1);
}
}else if(pattern[0] == '.'){
if(pattern[1] == '*'){
return match(str+1, pattern) || match(str, pattern+2);
}else{
return match(str+1, pattern+1);
}
}else{
if(pattern[1] == '*'){
return match(str, pattern+2) ;
}else{
return false;
}
}
}
使用递归更容易理解。首先对输入进行判断,如果有空指针返回false。然后寻找结束递归的条件,如果两个字符串同时到达最后'\0',则认为是适配成功的。如果非正则为0,正则不为0,有可能是a*这种情形,将pattern加2后再次进行适配。但是如果pattern == 0,str != 0,则肯定是不匹配的。结束递归的条件确定,进入正式逻辑
1.如果*pattern == *str。要考虑到aaaaa,a*的情况,这种情况match(str+1, pattern)这样处理。直到最后*str == 0,然后将a*忽略这样的逻辑。也可能bd和a*bd,此时就是match(str, pattern+2)。如果pattern[1] != '*',则将两个字符串都加1。
2.如果*pattern == '.' 。此时逻辑与上面相同
3.说明当前字符确实不匹配,这种情形只有一种情况可能返回true,即pattern[1] == '*',使用match(str, pattern+2)再次进行匹配。
以上全部为或逻辑,即只要有一种匹配方式为true,则表明正则表达式可以表示。
下面为《剑指offer》题解,里面回溯场景考虑重复,导致时间较长。时间为465 ms,上述方法为15 ms。多了一个match(str+1, pattern+2),为了解决ab和a*b这种情形,其实这种情形可以用match(str+1, pattern)来解决,变成b和a*b,再变成b和b。
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @param pattern string字符串
* @return bool布尔型
*
* C语言声明定义全局变量请加上static,防止重复定义
*/
int match_core(char* str, char* pattern){
if(str[0] == '\0' && pattern[0] == '\0'){
return 1;
}
if(str[0] != '\0' && pattern[0] == '\0'){
return 0;
}
if(pattern[1] == '*'){
if(pattern[0] == str[0] || (pattern[0] == '.' && str[0] != 0 )){
return match_core(str+1, pattern+2) ||
match_core(str+1, pattern) ||
match_core(str, pattern+2);
}else{
return match_core(str, pattern+2);
}
}
if(str[0] == pattern[0] || (pattern[0] == '.' && str[0] != 0 )){
return match_core(str + 1, pattern+1);
}
return 0;
}
int match(char* str, char* pattern ) {
if(str == NULL || pattern == NULL) {
return 0;
}
return match_core(str, pattern);
}
————————————————————————-——————————
这种递归解法其实算是暴力破解,里面多次计算了一些子问题,可以使用动态规划来进行求解,递推式非常难找,参考LeetCode
#include <string>
#include <vector>
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @param pattern string字符串
* @return bool布尔型
*/
bool is_match(string s, string p, int i, int j){
if(i == 0)
return false;
if(p[j-1] == '.'){
return true;
}
return s[i-1] == p[j-1];
}
bool match(string str, string pattern) {
int len1 = str.length(), len2 = pattern.length();
vector<vector <int>> mark(len1+1, vector<int>(len2+1));
mark[0][0] = true;
for(int i = 0 ; i <= len1 ; i++){
for(int j = 1; j <= len2; j++){
if(pattern[j-1] == '*'){
mark[i][j] = mark[i][j-2];
if(is_match(str, pattern, i, j-1)){
mark[i][j] = mark[i][j] || mark[i-1][j];
}
}else{
if(is_match(str, pattern, i, j)){
mark[i][j] = mark[i-1][j-1];
}
}
}
}
return mark[len1][len2];
}
};
如果是c语言就用二维数组代替vector,但是由于数组不能动态定义,所以写起来有点麻烦。
is_match表示第i个和第j个是否匹配,即s[i-1]和p[j-1]
mark[i][j]表示前i个和前j个字符是否匹配,p[j-1]代表第j个字符