【动态规划】正则表达式
1. 题目
题目列表:
序号 | 题目 | 难度 |
---|---|---|
1 | 10. 正则表达式匹配 | 困难 |
2. 应用
2.1. Leetcode 10. 正则表达式匹配
题目
解题思路
设 \(dp[i][j]\) 表示 \(s\) 的前 \(i\) 个字符与 \(p\) 的前 \(j\) 个字符是否匹配,即子串 \(s[0 \cdots i-1]\) 与子串 \(p[0 \cdots j-1]\) 是否匹配。
显然,当两个字符串的长度都是零时,它们可以匹配成功,因此,边界条件:
通过分析,容易看出来:
-
如果模式串 \(p\) 的某一个位置是一个普通字符(字母或者点号)时,它只能匹配字符串 \(s\) 中的一个字符;
-
如果模式串 \(p\) 的某一个位置是一个星号字符时,它可以多次匹配 \(s\) 中的多个相同的字符。
因此,对于 \(s[0 \cdots i-1]\) 和 \(p[0 \cdots j-1]\) 的最后一个字符,有三种情况:
-
当 \(p[j-1]\) 是字母时:
-
如果当前两个字符相等,即 \(s[i-1] = p[j-1]\),那么,当前状态可以通过前一个状态转移得到,即
\[dp[i][j] = dp[i-1][j-1], \ s[i-1] = p[j-1] \] -
如果当前两个字符不相等,即 \(s[i-1] \ne p[j-1]\),那么,两个字符串就不匹配,即
\[dp[i][j] = false, \ s[i-1] \ne p[j-1] \]
因此,当 \(p[j-1]\) 是字母时,其状态转移方程为:
\[dp[i][j] = \begin{cases} dp[i-1][j-1], &s[i-1] = p[j-1]\\ false, &s[i-1] \ne p[j-1] \end{cases} \] -
-
当 \(p[j-1]\) 是
*
时,那么,它可以对字符串 \(s\) 匹配任意次:-
如果星号匹配零次,即星号没有匹配到字符串 \(s\) 中的字符,那么,它可以通过前一个状态转移得到
\[dp[i][j] = dp[i][j-2], \ s[i-1] \ne p[j-2] \] -
如果星号匹配多次,那么,此时有两种情况:
-
当前匹配成功了,即消耗了一个字符串 \(s\) 中的字符 \(s[i - 1]\),当前状态与状态 \(dp[i - 1][j]\) 相同;
-
当前匹配结束了,此时,需要去掉模式串 \(p\) 中的 字母 + 星号 组合,当前状态与状态 \(dp[i][j-2]\) 相同。
上述两种情况,只要有一种情况匹配成功即可。
\[dp[i][j] = dp[i-1][j] \lor dp[i][j-2], \ s[i-1] = p[j-2] \] -
因此,当 \(p[j-1]\) 是字母时,其状态转移方程为:
\[dp[i][j] = \begin{cases} dp[i-1][j] \lor dp[i][j-2], &s[i-1] = p[j-2]\\ dp[i][j-2], &s[i-1] \ne p[j-2] \end{cases} \] -
-
当 \(p[j-1]\) 是
.
时,那么,它可以成功匹配字符串 \(s\) 中的任意字符;\[dp[i][j] = dp[i-1][j-1] \]
综上,状态转移方程为:
其中,\(match(x, y)\) 表示字符 \(x\) 和字符 \(y\) 是否匹配,当且仅当字符 \(x\) 和字符 \(y\) 相等或者字符 \(y\) 为 .
时,返回 true
。
注意:这里 \(\lor\) 表示逻辑或, \(\land\) 表示逻辑与。
注意:长度为零的模式串不能匹配任何字符串。
代码实现
class Solution {
public boolean isMatch(String s, String p) {
int m = s.length(), n = p.length();
boolean[][] dp = new boolean[m + 1][n + 1];
dp[0][0] = true;
for (int i = 0; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (p.charAt(j - 1) == '*') {
dp[i][j] |= dp[i][j - 2];
if (match(s, i, p, j - 1)) {
dp[i][j] |= dp[i - 1][j];
}
} else {
if (match(s, i, p, j)) {
dp[i][j] |= dp[i - 1][j - 1];
}
}
}
}
return dp[m][n];
}
private boolean match(String s, int i, String pattern, int j) {
// 判断s[i - 1]与p[j - 1]是否匹配
if (i == 0) {
return false;
}
if (pattern.charAt(j - 1) == '.') {
return true;
}
return s.charAt(i - 1) == pattern.charAt(j - 1);
}
}