目录
刷题日期: 16:2036 星期五2021年3月26日
个人刷题记录,代码收集,来源皆为leetcode
经过多方讨论和请教,现在打算往Java方向发力
主要答题语言为Java
题目:
剑指 Offer 19. 正则表达式匹配
难度困难192
请实现一个函数用来匹配包含'. '
和'*'
的正则表达式。模式中的字符'.'
表示任意一个字符,而'*'
表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"
与模式"a.a"
和"ab*ac*a"
匹配,但与"aa.a"
和"ab*a"
均不匹配。
示例 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 = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
s
可能为空,且只包含从a-z
的小写字母。p
可能为空,且只包含从a-z
的小写字母以及字符.
和*
,无连续的'*'
。
题目分析
本题的规则有点绕口,感觉自己都判断不出来结果。
对字符串的编程能力
对正则表达式的理解
如果为空的情况怎么去考虑
初始解答:
参考了书,尝试自己写出结果:
class Solution {
public boolean isMatch(String s, String p) {
if (s == null || p == null) return false; //输入空则直接返回错误
return isMatchCore(s, p); //否则调用另一个程序
private boolean isMatchCore(String s, String p) {
//需要考虑明白状态机,才能设计出对应的比较程序
if (s == '\0' && p == '\0') return true; //两者都为空的情况
if (s != '\0' && p == '\0') return false; //只有一个为空的情况
//书上的都不是字符串类型,没法类比
}
}
}
看了评论区,题目确实有点变态,用java的比较少,大家最后的结果都挺长的。
参考方法一,这道题要考虑的太多了:
学习他人:
方法一:
2020-05-26
递归,分情况处理
class Solution {
public boolean isMatch(String text, String pattern) {
char[] t = text.toCharArray();
char[] p = pattern.toCharArray();
return isMatchHelper(t, 0, p, 0);
}
private boolean isMatchHelper(char[] t, int index, char[] p, int pIndex) {
//匹配完成
if (index == t.length && pIndex == p.length) {
return true;
}
//未匹配完全
if (index != t.length && pIndex == p.length) {
return false;
}
//当前字符匹配判断
boolean matchSuc = index < t.length && (p[pIndex] == t[index] || p[pIndex] == '.');
if (p.length - pIndex >= 2 && p[pIndex + 1] == '*') {
//匹配0次或多次
return isMatchHelper(t, index, p, pIndex + 2) || (matchSuc && isMatchHelper(t, index + 1, p, pIndex));
}
return matchSuc && isMatchHelper(t, index + 1, p, pIndex + 1);
}
}
方法二:
有点东西,记住就完事
cute_aaa 2020-07-23
Java邪道(话说面试的时候不能这样写吗):
public boolean isMatch(String s, String p) {
return s.matches(p);
}
方法三:
Rory安杰尔L1 (编辑过)2021-03-16
上个官方题解的Java详细注释
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;//f[0][0]代表s和p均为空字符串,f[1][1]代表s和p的第一个字符(即在s和p中下标为0的字符)
for(int i = 0; i <= m ; ++i) {
for(int j = 1; j <= n; ++j) {
if(p.charAt(j - 1) == '*') {//p的第j个字符为*
if(matches(s, p, i, j - 1)) {//匹配s的第i个字符和p的第j-1个字符
f[i][j] = f[i - 1][j] || f[i][j - 2];//p中*前面的字符在s中出现多次或者在s中只出现1次
}
else {
f[i][j] = f[i][j - 2];//p中*前面的在s中字符出现0次
}
}
else {//p的第j个字符不为*
if(matches(s, p, i, j)) {//匹配s的第i个字符和p的第j个字符
f[i][j] = f[i - 1][j - 1];//匹配成功,状态转移;匹配不成功,默认是false
}
}
}
}
return f[m][n];
}
private 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);
}
}
方法四:
官方精选
作者:jerry_nju
链接:https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/solution/zhu-xing-xiang-xi-jiang-jie-you-qian-ru-shen-by-je/
来源:力扣(LeetCode)
class Solution {
public boolean isMatch(String A, String B) {
int n = A.length();
int m = B.length();
boolean[][] f = new boolean[n + 1][m + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
//分成空正则和非空正则两种
if (j == 0) {
f[i][j] = i == 0;
} else {
//非空正则分为两种情况 * 和 非*
if (B.charAt(j - 1) != '*') {
if (i > 0 && (A.charAt(i - 1) == B.charAt(j - 1) || B.charAt(j - 1) == '.')) {
f[i][j] = f[i - 1][j - 1];
}
} else {
//碰到 * 了,分为看和不看两种情况
//不看
if (j >= 2) {
f[i][j] |= f[i][j - 2];
}
//看
if (i >= 1 && j >= 2 && (A.charAt(i - 1) == B.charAt(j - 2) || B.charAt(j - 2) == '.')) {
f[i][j] |= f[i - 1][j];
}
}
}
}
}
return f[n][m];
}
}
可供选择的递归思路
class Solution {
public boolean isMatch(String A, String B) {
// 如果字符串长度为0,需要检测下正则串
if (A.length() == 0) {
// 如果正则串长度为奇数,必定不匹配,比如 "."、"ab*",必须是 a*b*这种形式,*在奇数位上
if (B.length() % 2 != 0) return false;
int i = 1;
while (i < B.length()) {
if (B.charAt(i) != '*') return false;
i += 2;
}
return true;
}
// 如果字符串长度不为0,但是正则串没了,return false
if (B.length() == 0) return false;
// c1 和 c2 分别是两个串的当前位,c3是正则串当前位的后一位,如果存在的话,就更新一下
char c1 = A.charAt(0), c2 = B.charAt(0), c3 = 'a';
if (B.length() > 1) {
c3 = B.charAt(1);
}
// 和dp一样,后一位分为是 '*' 和不是 '*' 两种情况
if (c3 != '*') {
// 如果该位字符一样,或是正则串该位是 '.',也就是能匹配任意字符,就可以往后走
if (c1 == c2 || c2 == '.') {
return isMatch(A.substring(1), B.substring(1));
} else {
// 否则不匹配
return false;
}
} else {
// 如果该位字符一样,或是正则串该位是 '.',和dp一样,有看和不看两种情况
if (c1 == c2 || c2 == '.') {
return isMatch(A.substring(1), B) || isMatch(A, B.substring(2));
} else {
// 不一样,那么正则串这两位就废了,直接往后走
return isMatch(A, B.substring(2));
}
}
}
}
方法五
k神,每道题都有他。
作者:jyd
链接:https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/solution/jian-zhi-offer-19-zheng-ze-biao-da-shi-pi-pei-dong/
来源:力扣(LeetCode)
class Solution {
public boolean isMatch(String s, String p) {
int m = s.length() + 1, n = p.length() + 1;
boolean[][] dp = new boolean[m][n];
dp[0][0] = true;
for(int j = 2; j < n; j += 2)
dp[0][j] = dp[0][j - 2] && p.charAt(j - 1) == '*';
for(int i = 1; i < m; i++) {
for(int j = 1; j < n; j++) {
dp[i][j] = p.charAt(j - 1) == '*' ?
dp[i][j - 2] || dp[i - 1][j] && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.') :
dp[i - 1][j - 1] && (p.charAt(j - 1) == '.' || s.charAt(i - 1) == p.charAt(j - 1));
}
}
return dp[m - 1][n - 1];
}
}
以上代码利用布尔运算实现简短长度,若阅读不畅,可先理解以下代码,与文中内容一一对应:
class Solution {
public boolean isMatch(String s, String p) {
int m = s.length() + 1, n = p.length() + 1;
boolean[][] dp = new boolean[m][n];
dp[0][0] = true;
// 初始化首行
for(int j = 2; j < n; j += 2)
dp[0][j] = dp[0][j - 2] && p.charAt(j - 1) == '*';
// 状态转移
for(int i = 1; i < m; i++) {
for(int j = 1; j < n; j++) {
if(p.charAt(j - 1) == '*') {
if(dp[i][j - 2]) dp[i][j] = true; // 1.
else if(dp[i - 1][j] && s.charAt(i - 1) == p.charAt(j - 2)) dp[i][j] = true; // 2.
else if(dp[i - 1][j] && p.charAt(j - 2) == '.') dp[i][j] = true; // 3.
} else {
if(dp[i - 1][j - 1] && s.charAt(i - 1) == p.charAt(j - 1)) dp[i][j] = true; // 1.
else if(dp[i - 1][j - 1] && p.charAt(j - 1) == '.') dp[i][j] = true; // 2.
}
}
}
return dp[m - 1][n - 1];
}
}
大佬就是大佬,恐怖如斯。
String 方法
下面是 String 类支持的方法,更多详细,参看 Java String API 文档:
总结
以上就是本题的内容和学习过程了,这道题要考虑的情况太多了,细细跟着大佬的思路学习一遍,以后碰到能记住s.matches§就可以了。
欢迎讨论,共同进步。