<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                這節課將分析三道比較難的題目,希望能幫助大家拓寬解題的思路。主要內容包括: * 正則表達式匹配 * 柱狀圖中的最大矩形 * 實現 strStr() 函數 #### 例題分析一 LeetCode 第 10 題,正則表達式匹配:給你一個字符串 s 和一個字符規律 p,請你來實現一個支持 '.' 和 '*' 的正則表達式匹配。 * '.' 匹配任意單個字符 * '*' 匹配零個或多個前面的那一個元素 > 注意:所謂匹配,是要涵蓋整個字符串 s 的,而不是部分字符串。 說明: * s 可能為空,且只包含從 a-z 的小寫字母。 * p 可能為空,且只包含從 a-z 的小寫字母,以及字符 . 和 *。 * 示例 1 ``` 輸入: s = "aa" p = "a" 輸出: false ``` 解釋: "a" 無法匹配 "aa" 整個字符串。 * 示例 2 ``` 輸入: s = "aa" p = "a*" 輸出: true ``` 解釋: 因為 '*' 代表可以匹配零個或多個前面的那一個元素, 在這里前面的元素就是 'a'。因此,字符串 "aa" 可被視為 'a' 重復了一次。 * 示例 3 ``` 輸入: s = "ab" p = ".*" 輸出: true ``` 解釋: ".*" 表示可匹配零個或多個('*')任意字符('.')。 * 示例 4 ``` 輸入: s = "aab" p = "c*a*b" 輸出: true ``` 解釋: 因為 '*' 表示零個或多個,這里 'c' 為 0 個, 'a' 被重復一次。因此可以匹配字符串 "aab"。 * 示例 5 ``` 輸入: s = "mississippi" p = "mis*is*p*." 輸出: false ``` 解釋:'p'與'i'無法匹配。 **解題思路** 不要害怕,這道題只要求實現正則表達式里的兩個小功能。 **判斷 s 和 p 匹配** 舉例:給定兩個字符串 s 和 p,判斷 s 和 p 是否匹配。 解法:s 和 p 必須要相等。定義兩個指針 i 和 j,分別指向 s 和 p 的第一個字符,然后逐個去比較,一旦發現不相等,就立即知道 s 和 p 不匹配。 此時,假設 s 字符串的長度為 m,p 字符串的長度為 n,s 和 p 匹配的條件就是 s 和 p 從頭到尾一直匹配,即 i == m 同時 j == n 是 s 和 p 匹配的唯一條件。 **點匹配符 '.'** '.' 匹配任意單個字符,首先要明確的是,它是一一對應關系,和 '*' 匹配符不一樣。舉例說明如下。 ``` 輸入: s = "leetcode" p = "l..tc..e" 輸出: true ``` 因為 '.' 可以匹配任何字符,即,一旦遇上了 '.' 匹配符,可以讓 i 指針和 j 指針同時跳到下一個位置。 **星匹配符?'*'?** '*' 匹配符較難,先要理解這個星匹配符的定義。題目“ '*' 匹配零個或多個前面的那一個元素”中包含三個重要的信息: 1. 它匹配的是 p 字符串中,該 '*' 前面的那個字符。 2. 它可以匹配零個或多個。 3. '*' 匹配符前面必須有一個非星的字符。 因此,在分析 '*' 匹配符的時候,一定要把 '*' 以及它前面的一個字符看作為一個整體, '*' 不能單獨作為一個個體來看(例如點匹配符)。例如,p 字符串是 a *,則把 (a*) 當作一個整體來看。 ?? 對 p 字符串說明如下。 * p 可以表示空字符串,因為 '*' 可以匹配 0 個前面的字符,即當有 0 個 a 的時候,為空字符串。 * a* 還能匹配一個 a,兩個 a,三個 a,一直到無窮個 a。 * 當 p 等于 '.*' 的時候,可以表示一個空字符串,也可以表示一個點,兩個點,三個點,一直到無窮個點。即它可以表示任何長度的一段字符串,包括空串。 舉例說明 ``` 輸入: s = "aaabcd" p = "ac*a*b.." ``` 1. 用兩個指針 i 和 j 分別指向 s 和 p 的開頭。 2. 在 p 字符串里,a 的下一個字符是 c,不是 '*',比較 s[i] 和 p[j]。因為它們都是字符 a,所以這個位置匹配正確,i 和 j 同時指向下一個。此時 j 的下一個字符是 '*',要將 c* 當作一個整體去看待。? ??? ? ? 3. 將 c* 看成是空字符,p 如下所示。 ? ? 4. 若匹配中不一致即 c* 不能當作空字符串,則當作一個 c 字符,此時 p 如下。 5. 若不行,則看作兩個 c。 以此類推,應用了回溯的思想。 對于將 c* 作為空字符串的情況。每一次,都要看看當前 j 指向的字符的下一個是不是 '*'。如果是 '*',就要作為整體考慮。很明顯,a 的下一個字符是 '*'。 ? 同樣,先將 a* 作為空字符串看待。此時,a != b,兩個字符串不匹配,因此回溯.現在將 a* 看成是一個 a,此時 a = a,兩個位置的字符匹配。 j 的下一個字符不是 '*',而是點號,比較 s[i] 和 p[j],發現 a != b。于是再次回溯,將 a* 看成是兩個 a,回到剛才的位置。 最后遇到了兩個點號,由于點號可以匹配任何字符,因此可以直接忽略。i 和 j 同時往前一步,再次遇到了點號。i 和 j 繼續往前一步。 ? 此時,i 和 j 都已經同時結束了各自的遍歷,表明 s 和 p 是匹配的。 > 提示:重點是把這種回溯的思想掌握好。對于這道題,可以采用遞歸的寫法,也可以采用動態規劃的寫法。 * [ ] 遞歸法一 一開始,用兩個指針 i 和 j 分別指向字符串 s 和 p 的第一個字符,當我們發現它們指向的字符相同時,我們同時往前一步移動指針 i 和 j。 接下來重復進行相同的操作,即,若將函數定義為 isMatch(String s, int i, String p, int j) 的話,通過傳遞 i 和 j,就能實現重復利用匹配邏輯的效果。 當遇到點匹配符的時候,方法類似。 來看看當遇到星匹配符的情況,舉例說明如下。要不斷地用 a* 去表示一個空字符串,一個 a,兩個 a,一直到多個 a…… 當 a* 表示空字符串的時候,i 和 j 應該如何調整呢?此時 i 保持不變,j+2,遞歸調用函數的時候,變成 isMatch(s, i, p, j + 2)。 此時,指向的字符和 j 指向的字符不匹配,于是回溯,回到原來的位置。11:57 用 a* 去表示一個 a,i 指向的字符與 a 匹配,那么 i+1。指針 j,已經完成了用 a* 去表示一個 a 的任務,接下來要指向 b,調用的時候應該是 isMatch(s, i + 1, p, j + 2)。 i 指向的字符和 j 指向的字符不匹配,又進行回溯,但是不用回到最開始的位置。已知用 a* 去表示空字符串不行,表示一個 a 也不行,那么嘗試兩個 a 字符,于是,i 再往前一步,用 a* 去匹配兩個 a,i 就到了 b 的位置上,調用的時候就是 isMatch(s, i + 2, p, j + 2)。 不斷地這樣操作,一旦遇到了 '*' 組合,就不斷地嘗試,直到最后滿足匹配;或者嘗試過所有的可能還是不行則表示 s 和 p 無法匹配。 代碼實現 根據上面的思路,一起來寫遞歸的實現。主體函數如下。 ``` boolean?isMatch(String?s,?String?p)?{ ????if?(s?==?null?||?p?==?null)?{ ????????return?false; ????} ???? ????return?isMatch(s,?0,?p,?0); } ``` 主體函數非常簡單,一開始做簡單的判斷,只要 s 和 p 有一個為 null,就表示不匹配。 > 注意:面試的時候,一定要注意對這些基本情況的考量,千萬不要認為輸入的值都是有效的。 接下來實現遞歸函數。 ``` boolean?isMatch(String?s,?int?i,?String?p,?int?j)?{ ????int?m?=?s.length(); ????int?n?=?p.length(); ?? ????//?看看pattern和字符串是否都掃描完畢 ????if?(j?==?n)?{ ????????return?i?==?m; ????} ?? ????//?next?char?is?not?'*':?必須滿足當前字符并遞歸到下一層 ????if?(j?==?n?-?1?||?p.charAt(j?+?1)?!=?'*')?{ ????????return?(i?<?m)?&&? ????????????(p.charAt(j)?==?'.'?||?s.charAt(i)?==?p.charAt(j))?&&? ????????????isMatch(s,?i?+?1,?p,?j?+?1); ????} ?? ????//?next?char?is?'*',?如果有連續的s[i]出現并且都等于p[j],一直嘗試下去 ????if?(j?<?n?-?1?&&?p.charAt(j?+?1)?==?'*')?{ ????????while?((i?<?m)?&&?(p.charAt(j)?==?'.'?||?s.charAt(i)?==?p.charAt(j)))?{ ????????????if?(isMatch(s,?i,?p,?j?+?2))?{ ????????????????return?true; ????????????} ????????????i++; ????????} ????} ?? ????//?接著繼續下去 ????return?isMatch(s,?i,?p,?j?+?2); } ``` 1. 函數接受四個輸入參數,s 字符串,p 字符串,i 指針,j 指針。 2. 開始時計算 s 字符串和 p 字符串的長度,分別記為 m 和 n。 3. 當 j 指針遍歷完了 p 字符串后,可以跳出遞歸,而 i 也剛好遍歷完,說明 s 和 p 完全匹配。 4. 判斷 j 字符的下一個是不是 '*',不是,則遞歸地調用 isMatch 函數,i + 1,j + 1。 5. 若是,則不斷地將它和 '*' 作為一個整體,分別去表示空字符串,一個字符,兩個字符,依此類推。如果其中一種情況能出現 s 和 p 的匹配,就返回 true。 6. while 循環是整個遞歸算法的核心,前提條件如下。 i 指向的字符必須要能和 j 指向的字符匹配,其中 j 指向的可能是點匹配符。 若無法匹配,i++,即用 '*' 組合去匹配更長的一段字符串。 當 i 字符和 j 字符不相同,或者 i 已經遍歷完了 s 字符串,同時 j 字符后面跟著一個 '*'的情況,只能用 '*'組合去表示一個空字符串,然后遞歸下去。 * [ ] 遞歸法二 上法是從前往后進行遞歸地調用,現在從后往前地分析這個問題。例如: ``` s = "aaabcd" p = "a*b.d" ``` 實現過程如下所示。 1. p 字符串的最后一個字符 d 必須要和 s 字符串的最后一個字符相同,才能使 p 有可能與 s 匹配,那么當它們都相同的時候,問題規模也縮小。 2. p 字符串的最后一個字符不是 '*',而是點號。它可以匹配 s 字符串里的任意一個字符,且它是最后一個,所以對應的就是 s 字符串里的 c,很明顯互相匹配,繼續縮小問題規模。 3. 同樣,b 不是 '*',比較它與 s 字符串的最后一個字符是否相同,是,則繼續縮小問題規模。 4. 遇到 '*','*'可以表示一個空字符串,與前一個字符表示空字符串的時候,將問題變成了判斷兩個字符串是否匹配,其中,s 等于 aaa,而 p 是空字符串,很明顯不能匹配。 5. 用 a* 去表示一個 a。 6. p 的最后一個還是 '*',用同樣的策略。 7. 繼續用 a* 去表示一個 a。 8. 用 a* 去表示空字符串。 9. 最后 s 和 p 都變成了空字符串,互相匹配。 代碼實現 主函數代碼如下。 ``` boolean?isMatch(String?s,?String?p)?{ ????if?(s?==?null?||?p?==?null)?return?false; ????return?isMatch(s,?s.length(),?p,?p.length()); } ``` 在主函數里,進行一些簡單基礎的判斷,如果 s 和 p 有一個是 null,則返回 false。 遞歸函數代碼如下。 ``` boolean?isMatch(String?s,?int?i,?String?p,?int?j)?{ ????if?(j?==?0)?return?i?==?0;? ????if?(i?==?0)?{ ????????return?j?>?1?&&?p.charAt(j?-?1)?==?'*'?&&?isMatch(s,?i,?p,?j?-?2); ????} ?? ????if?(p.charAt(j?-?1)?!=?'*')?{ ????????return?isMatch(s.charAt(i?-?1),?p.charAt(j?-?1))?&&? ???????????????isMatch(s,?i?-?1,?p,?j?-?1); ????} ?? ????return??isMatch(s,?i,?p,?j?-?2)?||?isMatch(s,?i?-?1,?p,?j)?&& ????????????isMatch(s.charAt(i?-?1),?p.charAt(j?-?2)); } boolean?isMatch(char?a,?char?b)?{ ????return?a?==?b?||?b?==?'.'; } ``` 1. 遞歸函數的輸入參數有四個,分別是字符串 s,當前字符串 s 的下標,字符串 p,以及字符串 p 的當前下標。由主函數可以看到,兩個字符串的下標都是從最后一位開始。 2. 若 p 字符串為空,并且如果 s 字符串也為空,就表示匹配。 3. 當 p 字符串不為空,而 s 字符串為空,如上例所示,當 s 為空字符串,而 p 等于 a*,此時只要 p 總是由 '*'組合構成,一定能滿足匹配,否則不行。 4. 若 p 的當前字符不是 '*',判斷當前的兩個字符是否相等,如果相等,就遞歸地看前面的字符。 5. 否則,如果 p 的當前字符是 '*': 用 '*' 組合表示空字符串,看看是否能匹配; 用 '*' 組合表示一個字符,看看能否匹配。 **動態規劃法** 遞歸的方法比較好理解,但是容易造成重疊計算。為了避免重疊計算,可以用動態規劃,自底向上地實現剛才的策略。 代碼實現 ``` //?分別用?m?和?n?表示?s?字符串和?p?字符串的長度 boolean?isMatch(String?s,?String?p)?{ ????int?m?=?s.length(),?n?=?p.length(); ????//?定義一個二維布爾矩陣?dp ????boolean[][]?dp?=?new?boolean[m?+?1][n?+?1]; ????//?當兩個字符串的長度都為?0,也就是空字符串的時候,它們互相匹配 ????dp[0][0]?=?true; ?? ????for?(int?j?=?1;?j?<=?n;?j++)?{ ????????dp[0][j]?=?j?>?1?&&?p.charAt(j?-?1)?==?'*'?&&?dp[0][j?-?2]; ????} ????for?(int?i?=?1;?i?<=?m;?i++)?{ ????????for?(int?j?=?1;?j?<=?n;?j++)?{ ????????????//?p?的當前字符不是?'*',判斷當前的兩個字符是否相等,如果相等,就看?dp[i-1][j-1]?的值,因為它保存了前一個匹配的結果 ????????????if?(p.charAt(j?-?1)?!=?'*')?{ ????????????????dp[i][j]?=?dp[i?-?1][j?-?1]?&&? ???????????????????isMatch(s.charAt(i?-?1),?p.charAt(j?-?1)); ????????????}?else?{ ????????????????dp[i][j]?=?dp[i][j?-?2]?||?dp[i?-?1][j]?&&? ???????????????????isMatch(s.charAt(i?-?1),?p.charAt(j?-?2)); ????????????} ????????} ????} ?? ????return?dp[m][n]; } boolean?isMatch(char?a,?char?b)?{ ????return?a?==?b?||?b?==?'.'; ? } ``` 注意: * 初始化二維矩陣第一行的所有值時,當 s 字符串為空,對于 p 字符串的任何一個位置,要使到這個位置的子串能和空字符串匹配,要求該子串都是由一系列的 '*' 組合構成。 * 對二維矩陣填表,運用到的邏輯跟遞歸一摸一樣。 * p 的當前字符不是 '*',判斷當前的兩個字符是否相等。如果相等,就看?dp[i-1][j-1]?的值,因為它保存了前一個匹配的結果。 * 如果 p 的當前字符是 '*': * 用 '*' 組合表示空字符串,能否匹配,也就是?dp[i][j - 2]; * 用 '*' 組合表示一個字符,能否匹配,也就是?dp[i - 1][j]。 * [ ] 復雜度分析 運用動態規劃,把時間復雜度控制在 O(n2),而空間復雜度也是 O(n2)。 建議:LeetCode 上有一道跟這題十分類似的題目,第 44 題,通配符匹配。分析例題的思路,所運用的策略,以及代碼的實現,都有很多非常相似的地方。大家一定要去做做看,舉一反三才能加深印象。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看