题目
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
‘.’ 匹配任意单个字符 ‘*’ 匹配零个或多个前面的那一个元素 所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
示例 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
提示:
0 <= s.length <= 20 0 <= p.length <= 30 s 可能为空,且只包含从 a-z 的小写字母。 p
可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。 保证每次出现字符 * 时,前面都匹配到有效的字符来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/regular-expression-matching
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目很好,可以当做某些数据进行匹配。也类似按某个匹配规则进行数据处理。不过这个题目提示到了正则的高度,可能比较通用化。通用化意味着包容性要强,也就是长度从0到无穷大都应该匹配上。
那么走二条路子。
对应是java 作业先交。是一个评论里面老哥写的比较好的
class Solution {
public boolean isMatch(String s, String p) {
if (s == null || p == null) return false;
int m = s.length(), n = p.length();
boolean[][] dp = new boolean[m + 1][n + 1];
dp[0][0] = true;
//"" 和p的匹配关系初始化,a*a*a*a*a*这种能够匹配空串,其他的是都是false。
// 奇数位不管什么字符都是false,偶数位为* 时则: dp[0][i] = dp[0][i - 2]
for (int i = 2; i <= n; i+= 2) {
if (p.charAt(i - 1) == '*') {
dp[0][i] = dp[0][i - 2];
}
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
char sc = s.charAt(i - 1);
char pc = p.charAt(j - 1);
if (sc == pc || pc == '.') {
dp[i][j] = dp[i - 1][j - 1];
} else if (pc == '*') {
if (dp[i][j - 2]) {
dp[i][j] = true;
} else if (sc == p.charAt(j - 2) || p.charAt(j - 2) == '.') {
dp[i][j] = dp[i - 1][j];
}
}
}
}
return dp[m][n];
}
}
然后我们来解读老哥的代码:
class Solution {
public boolean isMatch(String s, String p) {
/* 1.最基本的情况,其中一个有,另外一个完全没有,那么这二个字符串铁定不会匹配上 */
if (s == null || p == null) return false;
/* 获取对应字符串的长度 */
int m = s.length(), n = p.length();
/* 初始 对应一个boolean 二维数组 对应值是对应长度+1 前面一个是S 另外一个是P */
boolean[][] dp = new boolean[m + 1][n + 1];
/** 这里初始值 00 是true 这里类似赋值一个初始值,然后后面沿用 类似初始值是 true */
dp[0][0] = true;
//"" 和p的匹配关系初始化,a*a*a*a*a*这种能够匹配空串,其他的是都是false。
// 奇数位不管什么字符都是false,偶数位为* 时则: dp[0][i] = dp[0][i - 2]
/** 亮点 老哥这里的解释我觉得OJBK 存在匹配空串如果匹配上,必定是*号把前一位的进行对冲了。也就是利用* 标识匹配0个前面元素特性进行抵消 */
for (int i = 2; i <= n; i+= 2) {
if (p.charAt(i - 1) == '*') {
dp[0][i] = dp[0][i - 2];
}
}
/** 重头分析 : 上面优先排除集中极为特殊的情况,下面就是正常情况,区别在于多少问题
正常分析
*/
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
/** 获取要对比的字符 chanAt 获取对应下标 */
char sc = s.charAt(i - 1);
char pc = p.charAt(j - 1);
/** 简简单单的 如果对应字符匹配或者是. 万能匹配 那么对应就是OK 沿用之前的boolean dp [i-1][j-1] 就是最开始的00 就是ture 或者 有些地方的false */
if (sc == pc || pc == '.') {
dp[i][j] = dp[i - 1][j - 1];
} else if (pc == '*') {
/** 如果对应 pc是* 那么就是再前一位 对比值 如果dp[i][j - 2] 不正确那么进行下面的判断 */
if (dp[i][j - 2]) {
dp[i][j] = true;
} else if (sc == p.charAt(j - 2) || p.charAt(j - 2) == '.') {
/** 这二个判断应该是这里的核心关键 也就是动态规划的对比点
如果这上面的值对等,那么下面感觉还是差点东西。
我再领悟一下
*/
dp[i][j] = dp[i - 1][j];
}
}
}
}
return dp[m][n];
}
}
官方分析:
class Solution {
public boolean isMatch(String s, String p) {
int m = s.length();
int n = p.length();
boolean[][] f = new boolean[m + 1][n + 1];
f[0][0] = true;
for (int i = 0; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (p.charAt(j - 1) == '*') {
f[i][j] = f[i][j - 2];
if (matches(s, p, i, j - 1)) {
f[i][j] = f[i][j] || f[i - 1][j];
}
} else {
if (matches(s, p, i, j)) {
f[i][j] = f[i - 1][j - 1];
}
}
}
}
return f[m][n];
}
public boolean matches(String s, String p, int i, int j) {
if (i == 0) {
return false;
}
if (p.charAt(j - 1) == '.') {
return true;
}
return s.charAt(i - 1) == p.charAt(j - 1);
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/regular-expression-matching/solution/zheng-ze-biao-da-shi-pi-pei-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复杂度分析
时间复杂度:O(mn)O(mn),其中 mm 和 nn 分别是字符串 ss 和 pp 的长度。我们需要计算出所有的状态,并且每个状态在进行转移时的时间复杂度为 O(1)O(1)。
空间复杂度:O(mn)O(mn),即为存储所有状态使用的空间。
以上是对应官方解题思路。
不能都凭自己的理解去阅读一些东西,更需要有一个明确的官方的来引导自己的解题思路。
后续有新的理解,再做补充