掘金团队号上线,助你 Offer 临门! 点击 查看大厂春招职位
一、题目描述:
给你一个字符串 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 = "c*a*b"
输出:true
解释:因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入:s = "mississippi" p = "mis*is*p*."
输出:false
题目地址:leetcode-cn.com/problems/re…
二、思路分析:
使用递归。整体思路是:如果当前字符匹配成功,最终结果是之后的字符是否匹配成功。
定义递归函数recursion(String s,String p,int sIndex,int pIndex)
-
结束条件: sIndex >= s.length() || pIndex >= p.length()
需要注意的是:
当s匹配结束后,还要判断模式串后面是否有 'a*b*'等字符。 当p匹配结束后,当且仅当s也匹配结束时返回true。
-
当
p.charAt(pIndex+1) != '*'时,即 当前匹配的不是 '*'
当
p.charAt(pIndex) == s.charAt(sIndex)|| p.charAt(pIndex) == '.'时返回 recursion(s,p,sIndex+1,pIndex+1)
如果条件不成立,则返回 false。
-
当
p.charAt(pIndex+1) != '*'时,即当前匹配的是 '*'
使用贪心策略,如果当前位置的字符匹配,保持 pIndex不动,不断移动sIndex,每移动一次sIndex,
就判断一次 recursion(s,p,sIndex,pIndex+2)。如果 '*'后面的所有字符都匹配,
可以直接返回true。
如果当前位置的字符不匹配,即 '*' 已经匹配了足够多的字符(也有可能一个字符都没有匹配),
说明 '*' 匹配结束,返回 recursion(s,p,sIndex,pIndex+2)。
三、AC 代码:
class Solution {
public boolean isMatch(String s, String p) {
boolean[] match = new boolean[s.length() + 1];
match[s.length()] = true;
for (int i = p.length() - 1; i >= 0; i--) {
if (p.charAt(i) == '*') {// 如果是星号,从后往前匹配
for (int j = s.length() - 1; j >= 0; j--) {
match[j] = match[j] || (match[j + 1] && (p.charAt(i - 1) == '.' || (p.charAt(i - 1) == s.charAt(j))));
}// 记得把i多减一,因为星号是和其前面的字符配合使用的
i--;
} else {// 如果不是星号,从前往后匹配
for (int j = 0; j < s.length(); j++) {
match[j] = match[j + 1] && (p.charAt(i) == '.' || (p.charAt(i) == s.charAt(j)));
}// 只要试过了pattern中最后一个字符,就要把match[s.length()]置为false
match[s.length()] = false;
}
}
return match[0];
}
}
四、总结:
注意 ‘*’ 号是跟前面的字符配合使用的。还有一些边界条件,比如空串之类的。下面的测试 case 可以试下
"" "a*"
"ab" "c*a*b*d*"
"a" "c*.a*"
"abc" ".*"