0
点赞
收藏
分享

微信扫一扫

[动态规划]BM76 正则表达式匹配-较难

​​BM76 正则表达式匹配​​

知识点​​字符串​​​​动态规划​​​​递归​​

描述

请实现一个函数用来匹配包括'.'和'*'的正则表达式。1.模式中的字符'.'表示任意一个字符2.模式中的字符'*'表示它前面的字符可以出现任意次(包含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配

数据范围:1.str 只包含从 a-z 的小写字母。2.pattern 只包含从 a-z 的小写字母以及字符 . 和 *,无连续的 '*'。3. [动态规划]BM76 正则表达式匹配-较难_状态转移

4. [动态规划]BM76 正则表达式匹配-较难_递归_02



示例1

输入:

"aaa","a*a"

复制返回值:

true

复制说明:

中间的*可以出现任意次的a,所以可以出现1次a,能匹配上

示例2

输入:

"aad","c*a*d"

复制返回值:

true

复制说明:

因为这里 c 为 0 个,a被重复一次, * 表示零个或多个a。因此可以匹配字符串 "aad"。

示例3

输入:

"a",".*"

复制返回值:

true

复制说明:

".*" 表示可匹配零个或多个('*')任意字符('.')

示例4

输入:

"aaab","a*a*a*c"

复制返回值:

false


题解

递归解法

对于str[i]和pattern[k],如果:

  1. str[i] == pattern[k]或者pattern[k]是"."号
  1. 如果pattern[k+1]是*号,那么可能有三种匹配法,str[i]匹配pattern[k]和pattern[k+1],或者跳过pattern[k]和pattern[k+1],或者跳过pattern[k]和pattern[k+1]让str[i]和pattern[k+2]进行匹配
  1. str[i] != pattern[k]且pattern[k]是"."号:
  1. 如果pattern[k+1]是*号,则跳过pattern[k]和pattern[k+1],继续匹配str[i]和pattern[k+2]
  2. 如果pattern[k+1]不是*号,则匹配失败
#include <bits/stdc++.h>

using namespace std;

bool match_imp(const std::string &str, int str_index, std::string &pattern, int pat_index)
{
if (str_index > str.size() || pat_index > pattern.size())
{
return false;
}
if (str_index == str.size() && pat_index == pattern.size())
{
return true;
}
if (str_index == str.size())
{
// str已经走完,剩下的pattern必须是n个x*,x是任意小写字符或者.号
if (pat_index + 2 <= pattern.size() && pattern[pat_index + 1] == '*')
{
return match_imp(str, str_index, pattern, pat_index + 2);
}

return false;
}

if (pat_index == pattern.size())
{
return false;
}

if (pattern[pat_index] == str[str_index] || pattern[pat_index] == '.')
{
// 下一字符是*号
if (pat_index < pattern.size() - 1 && pattern[pat_index + 1] == '*')
{
return match_imp(str, str_index + 1, pattern, pat_index) || match_imp(str, str_index + 1, pattern, pat_index + 2) || match_imp(str, str_index, pattern, pat_index + 2);
}
// 下一字符不是*号
return match_imp(str, str_index + 1, pattern, pat_index + 1);
}

// 当前字符不匹配,则看后面是否有*号可以进行匹配
if (pat_index < pattern.size() - 1 && pattern[pat_index + 1] == '*')
{
return match_imp(str, str_index, pattern, pat_index + 2); // 跳过下2个pattern
}

return false;
}

bool match(string str, string pattern)
{
// '*'表示它前面的字符可以出现任意次(包含0次)
// '.'表示任意一个字符
if (str.empty() && (pattern.empty() || (pattern.size() == 1 && pattern.back() == '.')))
{
return true;
}
return match_imp(str, 0, pattern, 0);
}

动态规划解法

该解法来自于牛客官方题解

如果是只有小写字母,那么直接比较字符是否相同即可匹配,如果再多一个'.',可以用它匹配任意字符,只要对应str中的元素不为空就行了。但是多了'*'字符,它的情况有多种,涉及状态转移,因此我们用动态规划。

具体做法:

  • step 1:设dp[i][j]表示str前i个字符和pattern前j个字符是否匹配。(需要注意这里的i,j是长度,比对应的字符串下标要多1)
  • step 2:(初始条件) 首先,毋庸置疑,两个空串是直接匹配,因此[动态规划]BM76 正则表达式匹配-较难_字符串_03。然后我们假设str字符串为空,那么pattern要怎么才能匹配空串呢?答案是利用'*'字符出现0次的特性。遍历pattern字符串,如果遇到'*'意味着它前面的字符可以出现0次,要想匹配空串也只能出现0,那就相当于考虑再前一个字符是否能匹配,因此[动态规划]BM76 正则表达式匹配-较难_动态规划_04
  • step 3:(状态转移) 然后分别遍历str与pattern的每个长度,开始寻找状态转移。首先考虑字符不为'*'的简单情况,只要遍历到的两个字符相等,或是pattern串中为'.'即可匹配,因此最后一位匹配,即查看二者各自前一位是否能完成匹配,即[动态规划]BM76 正则表达式匹配-较难_动态规划_05。然后考虑'*'出现的情况:
  1. ​pattern[j - 2] == '.' || pattern[j - 2] == str[i - 1]​​:即pattern前一位能够多匹配一位,可以用'*'让它多出现一次或是不出现,因此有转移方程[动态规划]BM76 正则表达式匹配-较难_递归_06
  2. 不满足上述条件,只能不匹配,让前一个字符出现0次,[动态规划]BM76 正则表达式匹配-较难_递归_07.

图示:

[动态规划]BM76 正则表达式匹配-较难_动态规划_08


bool match(string str, string pattern)
{
int n1 = str.length();
int n2 = pattern.length();
// dp[i][j]表示str前i个字符和pattern前j个字符是否匹配
vector<vector<bool>> dp(n1 + 1, vector<bool>(n2 + 1, false));
//两个都为空串自然匹配
dp[0][0] = true;
//初始化str为空的情况,字符串下标从1开始
for (int i = 2; i <= n2; i++)
{
//可以让自己前面个字符重复0次
if (pattern[i - 1] == '*')
//与再前一个能够匹配空串有关
dp[0][i] = dp[0][i - 2];
}
//遍历str每个长度
for (int i = 1; i <= n1; i++)
{
//遍历pattern每个长度
for (int j = 1; j <= n2; j++)
{
//当前字符不为*,用.去匹配或者字符直接相同
if (pattern[j - 1] != '*' && (pattern[j - 1] == '.' || pattern[j - 1] == str[i - 1]))
{
dp[i][j] = dp[i - 1][j - 1];
//当前的字符为*
}
else if (j >= 2 && pattern[j - 1] == '*')
{
//若是前一位为.或者前一位可以与这个数字匹配
if (pattern[j - 2] == '.' || pattern[j - 2] == str[i - 1])
//转移情况
dp[i][j] = dp[i - 1][j] || dp[i][j - 2];
else
//不匹配
dp[i][j] = dp[i][j - 2];
}
}
}
return dp[n1][n2];
}
举报

相关推荐

0 条评论