字符串匹配,? 匹配單個任意字符,* 匹配任意長度字符串,包括空串。和第 10 題有些相似。html
直接按照以前第 10 題,修改一下就能夠了。java
一樣是用 dp[i][j] 表示全部的狀況,而後一層一層的根據遞推關係求出來。segmentfault
public boolean isMatch(String text, String pattern) { // 多一維的空間,由於求 dp[len - 1][j] 的時候須要知道 dp[len][j] 的狀況, // 多一維的話,就能夠把 對 dp[len - 1][j] 也寫進循環了 boolean[][] dp = new boolean[text.length() + 1][pattern.length() + 1]; // dp[len][len] 表明兩個空串是否匹配了,"" 和 "" ,固然是 true 了。 dp[text.length()][pattern.length()] = true; // 從 len 開始減小 for (int i = text.length(); i >= 0; i--) { for (int j = pattern.length(); j >= 0; j--) { // dp[text.length()][pattern.length()] 已經進行了初始化 if (i == text.length() && j == pattern.length()) continue; //相比以前增長了判斷是否等於 * boolean first_match = (i < text.length() && j < pattern.length() && (pattern.charAt(j) == text.charAt(i) || pattern.charAt(j) == '?' || pattern.charAt(j) == '*')); if (j < pattern.length() && pattern.charAt(j) == '*') { //將 * 跳過 和將字符匹配一個而且 pattern 不變兩種狀況 dp[i][j] = dp[i][j + 1] || first_match && dp[i + 1][j]; } else { dp[i][j] = first_match && dp[i + 1][j + 1]; } } } return dp[0][0]; }
時間複雜度:text 長度是 T,pattern 長度是 P,那麼就是 O(TP)。優化
空間複雜度:O(TP)。spa
一樣的,和第10題同樣,能夠優化空間複雜度。指針
public boolean isMatch(String text, String pattern) { // 多一維的空間,由於求 dp[len - 1][j] 的時候須要知道 dp[len][j] 的狀況, // 多一維的話,就能夠把 對 dp[len - 1][j] 也寫進循環了 boolean[][] dp = new boolean[2][pattern.length() + 1]; dp[text.length() % 2][pattern.length()] = true; // 從 len 開始減小 for (int i = text.length(); i >= 0; i--) { for (int j = pattern.length(); j >= 0; j--) { if (i == text.length() && j == pattern.length()) continue; boolean first_match = (i < text.length() && j < pattern.length() && (pattern.charAt(j) == text.charAt(i) || pattern.charAt(j) == '?' || pattern.charAt(j) == '*')); if (j < pattern.length() && pattern.charAt(j) == '*') { dp[i % 2][j] = dp[i % 2][j + 1] || first_match && dp[(i + 1) % 2][j]; } else { dp[i % 2][j] = first_match && dp[(i + 1) % 2][j + 1]; } } } return dp[0][0]; }
時間複雜度:text 長度是 T,pattern 長度是 P,那麼就是 O(TP)。code
空間複雜度:O(P)。htm
參考這裏,也比較好理解,利用兩個指針進行遍歷。blog
boolean isMatch(String str, String pattern) { int s = 0, p = 0, match = 0, starIdx = -1; //遍歷整個字符串 while (s < str.length()){ // 一對一匹配,兩指針同時後移。 if (p < pattern.length() && (pattern.charAt(p) == '?' || str.charAt(s) == pattern.charAt(p))){ s++; p++; } // 碰到 *,假設它匹配空串,而且用 startIdx 記錄 * 的位置,記錄當前字符串的位置,p 後移 else if (p < pattern.length() && pattern.charAt(p) == '*'){ starIdx = p; match = s; p++; } // 當前字符不匹配,而且也沒有 *,回退 // p 回到 * 的下一個位置 // match 更新到下一個位置 // s 回到更新後的 match // 這步表明用 * 匹配了一個字符 else if (starIdx != -1){ p = starIdx + 1; match++; s = match; } //字符不匹配,也沒有 *,返回 false else return false; } //將末尾多餘的 * 直接匹配空串 例如 text = ab, pattern = a******* while (p < pattern.length() && pattern.charAt(p) == '*') p++; return p == pattern.length(); }
時間複雜度:若是 str 長度是 T,pattern 長度是 P,雖然只有一個 while 循環,可是 s 並非每次都加 1,因此最壞的時候時間複雜度會達到 O(TP),例如 str = "bbbbbbbbbb",pattern = "*bbbb"。每次 pattern 到最後時,又會從新開始到開頭。遞歸
空間複雜度:O(1)。
在第10題中還有遞歸的解法,但這題中若是按照第 10 題的遞歸的思路去解決,會致使超時,目前沒想到怎麼在第 10 題的基礎上去改,有好的想法你們能夠和我交流。
若是非要用遞歸的話,能夠按照動態規劃那個思路,先壓棧,而後出棧過程其實就是動態規劃那樣了。因此其實不如直接動態規劃。
動態規劃的應用,理清遞推的公式就能夠。另外迭代的方法,也讓人眼前一亮。
更多詳細通俗題解詳見 leetcode.wang 。