LeetCode 力扣 44. 通配符匹配

題目描述(困難難度)

字符串匹配,? 匹配單個任意字符,* 匹配任意長度字符串,包括空串。和第 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
相關文章
相關標籤/搜索