考研完之后一直很浮躁,也没做啥事儿,现在疫情这么严重,出不了家门,初始公布成绩也推迟了,还有十来天就要出成绩了,呼……先做毕设写算法,出成绩之后再打算趴。
10 正则表达式
之前试着提交过这个题目,但是没过,今天补上。
解题报告里有两种方法,回溯,动态规划。自己写的时候用回溯,但是代码及其不简洁而且考虑也不周全。解题报告里的回溯代码可真是清爽。
回溯法
如果没有符号 * 的存在这个题目就会很简单,* 符号代表匹配前一个字符0次或者多次,另外 . 符号表示着任意字符。
这样一来 * 符号有两种情况:
- 匹配前一个字符 0 次
- 匹配前一个字符 多 次
所以,分情况来看:( i, j 分别表示s,p字符串的位置)
p[j+1] != '*' p[i] == s[i] || p[j] == '.'
也就是说 s[i] p[j] 可以匹配,并且p[j]后面的字符不是* :令s[i+1]和p[j+1]后面的字符串去匹配p[j+1] == '*'
这时候就要分两种情况来看,
匹配0次的时候:去比较s[i]和p[j+2]之后的字符串,是否匹配成功
匹配多次的时候:s[i]==p[j] 且 s[i+1]和p[j]之后的字符串可以匹配成功
代码:
public boolean isMatch(String s, String p) {
if (p.isEmpty()) return s.isEmpty(); //两者为“” 则为true
// 第一个字符是否匹配
boolean first = (!s.isEmpty()) && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.');
if (p.length() >= 2 && p.charAt(1) == '*') {//有*的时候
// || 前面为匹配0次的情况 “aab, a*c*b”
// || 后面为匹配多次的情况 “aaab, a*b”
return (isMatch(s, p.substring(2))) || (first && isMatch(s.substring(1), p));
} else {
return first && isMatch(s.substring(1), p.substring(1));
}
}
动态规划
用dp[i][j]
表示s前i个字符是否可以被p的前j个字符匹配成功。
根据dp[i-1][j-1]
来判断dp[i][j]
p[j] != '*'
p[j] == s[i] || p[j] == '.'
:p[i][j] = p[i-1][j-1]
p[j] == '*'
p[j-1] != s[i]
:s[i]
和*
前面的字符匹配不上, 但并不代表是匹配失败,还有可能*前的字符匹配0次,这是需要看dp[i][j-2]
的情况:dp[i][j] = dp[i][j-2]
p[j-1] == s[i]
: 同*
前的字符匹配上,但这也不能代表s的前i个字符可以被p的前j个字符匹配,此时还是两种情况:匹配0次,即dp[i][j-2]
的情况;匹配多次,即dp[i-1][j]
的情况;两种情况有一种成功,即成功
代码:
public boolean isMatch(String s, String p) {
boolean[][] dp = new boolean[s.length()+1][p.length()+1];
dp[0][0] = true;
// 相当于初始 "" 被p匹配的情况
for (int i = 0; i < p.length(); i++) {
if (p.charAt(i)=='*' && dp[0][i-1]) dp[0][i+1] = true;
}
for (int i = 0; i < s.length(); i++) {
for (int j = 0; j < p.length(); j++) {
// s[i] 可以和 p[j] 匹配的情况,看dp前面
if (s.charAt(i) == p.charAt(j) || p.charAt(j) == '.'){
dp[i+1][j+1] = dp[i][j];
}
if (p.charAt(j) == '*'){
// s[i] 和 p[j] 前的字符匹配不上 考虑匹配0次的情况
if (p.charAt(j-1) != s.charAt(i) && p.charAt(j-1) != '.'){
dp[i+1][j+1] = dp[i+1][j-1];
}else{
//s[i] 和 p[j] 前的字符匹配上 需要考虑匹配0次和匹配多次的情况
dp[i+1][j+1] = dp[i+1][j-1] || dp[i][j+1];
}
}
}
}
return dp[s.length()][p.length()];
}
代码中的dp[i][j] 和 s[i] p[j] 中的i和j分别代表各自在各种中的索引