LeetCode 44. 通配符匹配
阿新 • • 發佈:2018-10-06
ring 如果 狀態 支持 形式 tag 給定 color ret
給定一個字符串 (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) { returnfalse; } 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]。
LeetCode 44. 通配符匹配