题目
给你一个字符串 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
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
代码
package org.example;
import java.util.Arrays;
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
public static void main(String[] args) {
String s = "heeeeeeeee";
String p = "he*";
boolean res = isMatch2(s, p);
System.out.println(res);
}
public static boolean isMatch(String s, String p) {
// dp[i][j] 表示 s 的前 i 个字符与 p 的前 j 个字符是否匹配
boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
// 初始化 dp[0][0] 为 true
dp[0][0] = true;
// 处理模式中的 *
for (int j = 1; j <= p.length(); j++) {
if (p.charAt(j - 1) == '*') {
dp[0][j] = dp[0][j - 2]; // * 匹配 0 个字符
}
}
// 填充 dp 数组
for (int i = 1; i <= s.length(); i++) {
for (int j = 1; j <= p.length(); j++) {
char sChar = s.charAt(i - 1);
char pChar = p.charAt(j - 1);
if (pChar == '.' || pChar == sChar) {
dp[i][j] = dp[i - 1][j - 1]; // 字符匹配
} else if (pChar == '*') {
dp[i][j] = dp[i][j - 2] || (dp[i - 1][j] && (sChar == p.charAt(j - 2) || p.charAt(j - 2) == '.'));
}
}
}
for (int i = 0; i < dp.length; i++) {
System.out.println(Arrays.toString(dp[i]));
}
// System.out.println(Arrays.deepToString(dp));
return dp[s.length()][p.length()];
}
public static boolean isMatch2(String s, String p) {
//.1 创建bp数组, 表示s的前i个字符和s的前p个符合
boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
// 给默认值,
dp[0][0] = true;
/*
* 2. 给基础值
* 当前匹配的值是*的时候,就判断前面的是否符合。
* todo 这里为什么不对.做额外判断
* 当是.的时候,虽然可以是任意数,但是还需要前面的完全匹配才可以。
*
*/
for (int j = 1; j <= p.length(); j++) {
// 如果当前规则是*,
if (p.charAt(j - 1) == '*') {
dp[0][j] = dp[0][j - 2];
}
}
for (int i = 1; i <= s.length(); i++) {
for (int j = 1; j <= p.length(); j++) {
// 1. 获取到当前判断的字符
char sChar = s.charAt(i - 1);
char pChar = p.charAt(j - 1);
/*
* @example 1
* 当pChar是.或者字符的时候,
* 这个时候表示必须要符合条件,和上一个字符也是可以匹配的。
*
* @example2
* 当pChar是*的时候
* 当是*的时候前面的字符可以出现0次或者n次,所以我们这里需要判断他前面的两个字符
* s = 'hello'
* p = 'h.*'
* 因为当为*的是欧前面的字符可以出现0次或者n次。
*
* 出现0次情况,
* 所以这里判断的是前面的字符,所以我们要进行-1
* 又因为前面的字符可以出现0次,所以我们需要对前面前面的字符进行判断所以进行-1
* 上面说过我们的dp的表示s的前i个字符与p的前j个是否符合
* 由此得出一个公式
* 当判断*的时候
* dp[i][j] = dp[i][j-2]
*
* 出现一次的情况
* s he
* p he*
* 当s匹配到he和p到了he*的时候。
* 其实也就是在查看he和he是否符合。
* 由此我们可以得出出现一次的时候公式为
* dp[i][j] = dp[i][j-1]
*
* 出现多次的情况
* s heee
* p he*,
* 当p匹配到了*的时候,其实就是在看*前面的匹配规则是否符合
* 当s到了heee
* 我们其实只需要判断hee的时候是不是符合,并且P[j]是不是为','或者p[j]等于s[1]
* 所以可以得到公式
* dp[i][j] = dp[i-1][j] && p[j-2] == '.' || p[j - 2] == s[i-1]
*/
if (sChar == pChar || pChar == '.') {
// @example
dp[i][j] = dp[i - 1][j - 1];
} else if (pChar == '*') {
// @example2
dp[i][j] = dp[i][j - 2] || dp[i][j - 1] || (dp[i - 1][j] && (p.charAt(j - 2) == '.' || p.charAt(j - 2) == sChar));
}
}
}
for (int i = 0; i < dp.length; i++) {
System.out.println(Arrays.toString(dp[i]));
}
// System.out.println(Arrays.deepToString(dp));
return dp[s.length()][p.length()];
}
}