给定一个字符串 (s
) 和一个字符模式 (p
) ,实现一个支持 ‘?‘
和 ‘*‘
的通配符匹配。
‘?‘ 可以匹配任何单个字符。 ‘*‘ 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。
说明:
s
可能为空,且只包含从a-z
的小写字母。p
可能为空,且只包含从a-z
的小写字母,以及字符?
和*
。
示例 1:
输入: s = "aa" p = "a" 输出: false 解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入: s = "aa" p = "*" 输出: true 解释: ‘*‘ 可以匹配任意字符串。
示例 3:
输入: s = "cb" p = "?a" 输出: false 解释: ‘?‘ 可以匹配 ‘c‘, 但第二个 ‘a‘ 无法匹配 ‘b‘。
示例 4:
输入: s = "adceb" p = "*a*b" 输出: true 解释: 第一个 ‘*‘ 可以匹配空字符串, 第二个 ‘*‘ 可以匹配字符串 "dce".
示例 5:
输入: s = "acdcb" p = "a*c?b" 输入: false
这个题在很多个Tag下,如DP,贪心,回溯等。
动态规划
对于两个字符串的问题,容易想到用dp[i][j]这样的形式来表示状态。我一开始是定义dp[i][j]为字符串s中前i个字符和p中前j个字符是否匹配(i和j从0开始),由于初始化问题弄了很久,最后写出的代码异常垃圾。。。
class Solution { public: bool isMatch(string s, string p) { if (p.size() == 0 && s.size() == 0) { return true; } if (p.size() == 0) { return false; } else if (p == "*") { return true; } else if (s.size() == 0) { return false; } int row = p.size(), column = s.size(); vector<vector<int>> dp(row); vector<bool> okDp(row, false); for (int i = 0; i < row; i++) { dp[i].resize(column); } bool matched = false; dp[0][0] = (p[0] == ‘*‘ || p[0] == ‘?‘ || p[0] == s[0]) ? 1 : 0; if (p[0] == ‘?‘ || p[0] == s[0]) { matched = true; } for (int i = 1; i < row; i++) { if (p[i] == ‘*‘) { dp[i][0] = dp[i - 1][0]; if (dp[i][0]) { okDp[i] = true; } } else if (p[i - 1] == ‘*‘ && p[i] == s[0]) { dp[i][0] = matched ? 0 : dp[i - 1][0]; matched = true; } else if (p[i] == ‘?‘) { dp[i][0] = matched ? 0 : dp[i - 1][0]; matched = true; } } for (int j = 1; j < column; j++) { if (p[0] == ‘*‘) { dp[0][j] = 1; } } for (int i = 1; i < row; i++) { for (int j = 1; j < column; j++) { if (isalpha(s[j]) && isalpha(p[i])) { if (s[j] == p[i]) { dp[i][j] = dp[i - 1][j - 1]; } else { dp[i][j] = 0; } } else if (p[i] == ‘?‘) { dp[i][j] = dp[i - 1][j - 1]; } else if (p[i] == ‘*‘) { if (okDp[i] || dp[i - 1][j]) { okDp[i] = true; dp[i][j] = 1; } } } } return dp[row - 1][column - 1]; } };
后来学习了其他的解法,先上代码:
class Solution { public: bool isMatch(string s, string p) { int n=s.size(); int m=p.size(); //代表的是 s[0...i] p[0..j]是否匹配的情况 vector<vector<bool>>dp(n+1,vector<bool>(m+1,false)); dp[0][0]=true; for(int j=1;j<=m;j++) dp[0][j]=(p[j-1]==‘*‘?dp[0][j-1]:0); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(i>0&&(s[i-1]==p[j-1]||p[j-1]==‘?‘)) dp[i][j]=dp[i-1][j-1]; else if(p[j-1]==‘*‘) { dp[i][j]=dp[i-1][j-1]||dp[i-1][j]||dp[i][j-1]; } else dp[i][j]=0; } return dp[n][m]; } };
在这里,状态dp[i][j]是从1开始计算起来的。一开始不明白为什么这样子也行得通,后来考虑到有空串的情况。如""和""对于dp[0][0],""和"*"对于dp[0][1]。这样子的话初始化就很容易了。
在填表的过程中:
- 如果s[i - 1] = p[j - 1],则是字母匹配到了字母,那么匹配结果取决于前面的匹配结果。
- 如果p[j - 1] = ‘?‘,那么是问号匹配到了字母,也是匹配结果取决于前面的结果。
- 如果p[j - 1] = ‘*’,那么是遇到了星号。星号可能匹配的是空串,那么就是取决于dp[i][j - 1]的结果。如果匹配了一个字符,那么就是取决于dp[i - 1][j - 1]。如果匹配了两个字符,那么就是取决于dp[i - 2][j - 1]。
所以,dp[i][j] = dp[i][j - 1] || dp[i - 1][j - 1] || dp[i - 2][j - 1] || ... || dp[0][j - 1]。
又因为dp[i - 1][j] = dp[i - 1][j - 1] || dp[i - 2][j - 1] || ... || dp[0][j - 1]。
所以dp[i][j] = dp[i][j - 1] || dp[i - 1][j]。
原文地址:https://www.cnblogs.com/hlk09/p/9748225.html
时间: 2024-10-15 01:21:48