1、题目描述
【JZ52】请实现一个函数用来匹配包括'.'
和'*'
的正则表达式。模式中的字符'.'
表示任意一个字符,而'*'
表示它前面的字符可以出现任意次(包含0次)。
在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"
与模式"a.a"
和"abaca"匹配,但是与"aa.a"
和"ab*a"
均不匹配。
知识点:字符串,动态规划,递归
难度:☆☆☆
2、解题思路
字符串的匹配是把目标串和模式串的每一个字符挨个比较,每一次比较,都会针对三种情况进行讨论,三种情况分别是:普通字符、'*'
字符、‘.’
字符。
1、当是普通字符时,一样则匹配,不一样则不匹配;
2、当是‘.’
字符时,直接匹配;
3、当是'*'
字符时,'*'
字符表示前一个字符重复 0 次或者多次,又可以分成两种情况讨论:
3.1 重复 0 次,那么目标串回去前一个字符,模式串走下一个字符,重新匹配;
3.2 重复 1 次或多次,那么目标串不断往下遍历,比如目标串走了 3 步就开始和 '*'
字符 前面的字符不匹配,就可以当成重复了 3 次。
图解如下:
情况1:普通字符,且匹配,目标串和模式串都往下走一步,继续判断;
情况2:'.'
字符,直接匹配,目标串和模式串都往下走一步,继续判断;
情况3:当前元素匹配了,发现下一个模式串字符是'*'
:
3.1 当做重复了 0 次,目标串不动,模式串走 2 步;
3.2 当重复了 1 或多次,模式串不动,目标串不断往下走,每走一步就判断一次。
1、递归函数功能:match(s, p) -> bool
, 表示p
是否可以匹配s
;
2、递归终止条件:
2.1 如果s 和 p 同时为空,表明正确匹配;
2.2 如果s不为空,p为空,表明,不能正确匹配;
2.3 如果s为空,p不为空,需要计算,不能直接给出结果;
3、解题代码
package pers.klb.jzoffer.hard;
/**
* @program: JzOffer2021
* @description: 正则表达式匹配
* @author: Meumax
* @create: 2020-07-22 09:42
**/
public class MatchString {
public boolean match(char[] str, char[] pattern) {
//return matchTwo(str, 0, str.length, pattern, 0, pattern.length);
return doMatch(str, pattern, 0, 0);
}
private boolean doMatch(char[] str, char[] pattern, int strIndex, int patternIndex) {
// 目标串和模式串都为空串,匹配
// str.length - strIndex 表示:包含当前字符在内,还有多少个字符没有匹配
// pattern.length - patternIndex 表示:包含当前模式字符在内,还有多少个模式字符没用上
if ((str.length - strIndex == 0) && (pattern.length - patternIndex == 0)) {
return true;
}
// 目标串不为空,模式串为空,不匹配
if ((str.length - strIndex != 0) && (pattern.length - patternIndex == 0)) {
return false;
}
// 目标串遍历完了,模式串可以遍历完也可以没有遍历完
if (str.length - strIndex == 0) {
// 如果模式串还有剩余字符,遍历剩余字符,看是否满足条件
while (pattern.length - patternIndex != 0) {
// 不满足的情况1:只剩下一个字符,且这个字符还不为 ‘*’,比如:'a' 或者 '.'
// 不满足的情况2:剩下不止一个字符,第 0 个字符不为 ‘*’ 也就算了,第 1 个字符还是不为 ‘*’。比如:'a' '.'
if (pattern[patternIndex] != '*' && (pattern.length - patternIndex - 1 == 0 || pattern[patternIndex + 1] != '*')) {
return false;
}
// 遍历下一个字符
patternIndex++;
}
// while结束还没 return ,说明剩余的字符都满足要求
// 满足的情况:剩余的模式串字符肯定要用*来消除,比如说:a * b * c *,这种结构的无论多长,都可以因为 * 表示 0 个来消除。
return true;
}
// 目标串先不管,模式串已经遍历到最后一个字符了
if (pattern.length - patternIndex == 1) {
if (str[strIndex] == pattern[patternIndex] || pattern[patternIndex] == '.')
// 如果目标串当前字符和模式串当前字符能匹配,则继续往下匹配
return doMatch(str, pattern, strIndex + 1, patternIndex + 1);
else {
// 当前字符不匹配
return false;
}
}
// 目标串和模式串都不为空
if (pattern[patternIndex + 1] != '*') { // 模式串下一个字符不是 *
// 判断当前字符是否匹配
if ((str[strIndex] == pattern[patternIndex] || pattern[patternIndex] == '.')) {
// 匹配,则继续匹配下一个字符
return doMatch(str, pattern, strIndex + 1, patternIndex + 1);
} else {
// 不匹配则返回 false
return false;
}
} else { // 模式串下一个字符是 *
boolean flag = false;
// 重复 1 次或多次
if ((str[strIndex] == pattern[patternIndex] || pattern[patternIndex] == '.')) {
flag = doMatch(str, pattern, strIndex + 1, patternIndex);
}
// 重复 0 次和重复 1 到多次的综合结果
return flag || doMatch(str, pattern, strIndex, patternIndex + 2);
}
}
}
4、解题心得
本题难度适中,就是对所有的情况不断的 if 和递归操作,比较繁琐。