<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          每日一道 LeetCode (9):實現(xiàn) strStr()

          共 1128字,需瀏覽 3分鐘

           ·

          2020-08-05 23:58

          ?

          每天 3 分鐘,走上算法的逆襲之路。

          ?

          前文合集

          每日一道 LeetCode 前文合集

          代碼倉庫

          GitHub:https://github.com/meteor1993/LeetCode

          Gitee:https://gitee.com/inwsy/LeetCode

          題目:實現(xiàn) strStr()

          題目來源:https://leetcode-cn.com/problems/implement-strstr/

          實現(xiàn) strStr()?函數(shù)。

          給定一個 haystack 字符串和一個 needle 字符串,在 haystack 字符串中找出 needle 字符串出現(xiàn)的第一個位置 (從0開始)。如果不存在,則返回? -1。

          示例 1:

          輸入:?haystack?=?"hello",?needle?=?"ll"
          輸出:?2

          示例 2:

          輸入:?haystack?=?"aaaaa",?needle?=?"bba"
          輸出:?-1

          說明:

          當(dāng)?needle?是空字符串時,我們應(yīng)當(dāng)返回什么值呢?這是一個在面試中很好的問題。

          對于本題而言,當(dāng)?needle?是空字符串時我們應(yīng)當(dāng)返回 0 。這與 C 語言的?strstr()?以及 Java的?indexOf()?定義相符。

          解題思路:暴力方案

          解題思路?

          這道題還搞啥解題思路?

          題目都直接把答案寫出來了,「這與 C 語言的?strstr()?以及 Java的?indexOf()?定義相符」,我直接用 indexOf() 它不香么?

          public?int?strStr(String?haystack,?String?needle)?{
          ????return?haystack.indexOf(needle);
          }

          看著效率,杠杠的,我可真是個小機靈鬼。

          但是如果你在面試的時候這么答,會不會被面試打個半死我就不知道了。

          那么接下來,最符合常人的暴力思路來襲,我就喜歡干這事兒。

          借一張官方的圖:

          我做一個循環(huán),直接比較 needle 長度的字符串,如果相等就可以直接返回了,如果比到最后沒比出來,就返回 -1 ,解題結(jié)束。

          我就是這么的直接。。。以及。。。暴力。。。

          能用暴力解決的問題,絕不多動腦子。

          public?int?strStr_1(String?haystack,?String?needle)?{

          ????int?h?=?haystack.length(),?n?=?needle.length();

          ????for?(int?i?=?0;?i?1;?i++)?{
          ????????if?(needle.equals(haystack.substring(i,?i?+?n)))?{
          ????????????return?i;
          ????????}
          ????}

          ????return?-1;
          }

          好像結(jié)果也還算可以嘛,沒有那種慢到不可接受。

          解題思路:暴力方案優(yōu)化

          做完題好習(xí)慣看看答案,然后知道了我上面的這種暴力方案是基于一個叫 「滑動窗口」 的東西,這個名字倒是蠻形象的。

          上面的暴力方案有一個缺點是,會將 haystack 所有長度為 n 的子串都和 needle 做比較,那么能不能少比較幾次呢?

          當(dāng)然是可以的,以下內(nèi)容來源于官網(wǎng):

          1. 第一件事兒就是只有第一個字符相等的才有比較的意義,如果第一個字符都不相等,這也就不用比了(圖片來源于官方)。
          1. 接著一個字符一個字符比較,一旦不匹配了就立刻終止(圖片來源于官方)。

          截止到目前,都還是很好理解的,下面這一步就稍微有點抽象了,而這個方案的精髓也是下面這一步。

          1. 這里,比較到最后一位的時候發(fā)現(xiàn)不匹配,開始回溯。需要注意的是,pn 指針是移動到 pn = pn - curr_len + 1 的位置(圖片來源于官方)。
          1. 在這之后,接著 ++pn ,尋找開頭和 needle 第一位相同的子串,找到之后重復(fù)上面的比較的過程,然后找到了答案置(圖片來源于官方)。

          實現(xiàn)代碼如下(代碼來自于官方):

          public?int?strStr_2(String?haystack,?String?needle)?{
          ????int?L?=?needle.length(),?n?=?haystack.length();
          ????if?(L?==?0)?return?0;

          ????int?pn?=?0;
          ????while?(pn?1)?{
          ????????//?第一次循環(huán)?pn?,尋找和?needle?第一位相同的子串
          ????????while?(pn?1?&&?haystack.charAt(pn)?!=?needle.charAt(0))?++pn;

          ????????//?從?pn?開始,按位比較字符,獲得相同位數(shù)長度?currLen
          ????????int?currLen?=?0,?pL?=?0;
          ????????while?(pL?????????????++pn;
          ????????????++pL;
          ????????????++currLen;
          ????????}

          ????????//?如果?currLen?長度等于?needle?長度,匹配結(jié)束
          ????????if?(currLen?==?L)?return?pn?-?L;

          ????????//?如果不等于,開始回溯
          ????????pn?=?pn?-?currLen?+?1;
          ????}
          ????return?-1;
          }

          這段代碼自己做了字符的循環(huán)比較,但是很不幸,這種比較方案要比使用 equals() 來的要慢,我稍微修改下:

          public?int?strStr_3(String?haystack,?String?needle)?{
          ????int?L?=?needle.length(),?n?=?haystack.length();
          ????if?(L?==?0)?return?0;
          ????int?pn?=?0;
          ????while?(pn?1)?{
          ????????//?第一次循環(huán)?pn?,尋找和?needle?第一位相同的子串
          ????????while?(pn?1?&&?haystack.charAt(pn)?!=?needle.charAt(0))??++pn;
          ????????//?如果?pn?+?L?的長度大于當(dāng)前字符串長度,直接返回?-1
          ????????if?(pn?+?L?>?n)?return?-1;
          ????????//?如果?pn?+?L?得到的子串和?needle?相同,直接返回?pn
          ????????if?(haystack.substring(pn,?pn?+?L).equals(needle))?{
          ????????????return?pn;
          ????????}
          ????????//?沒匹配到?++pn
          ????????++pn;
          ????}
          ????return?-1;
          }

          思路還是同樣的思路,但是我在字符串的比較換成了 equals() ,耗時重回 1ms 。

          拋磚引玉

          到這里,我們往回看一個問題,為啥 jdk 提供的 indexOf() 這個方法,可以把耗時壓縮到 0ms ?為何 indexOf() 這個方法如此 NB ?

          點開源碼,找到核心方法(jdk 版本: 1.8.0_221):

          static?int?indexOf(char[]?source,?int?sourceOffset,?int?sourceCount,
          ????????char[]?target,?int?targetOffset,?int?targetCount,
          ????????int?fromIndex)
          ?
          {
          ????//?1、當(dāng)開始查找位置?大于等于?源字符串長度時,如果[查找字符串]為空,則:
          ????//?返回字符串的長度,否則返回-1.
          ????if?(fromIndex?>=?sourceCount)?{
          ????????return?(targetCount?==?0???sourceCount?:?-1);
          ????}
          ????// 2、如果 fromIndex 小于?0?,則從?0?開始查找。
          ????if?(fromIndex?0)?{
          ????????fromIndex?=?0;
          ????}
          ????//?3、如果[查找字符串]為空,則返回?fromIndex
          ????if?(targetCount?==?0)?{
          ????????return?fromIndex;
          ????}
          ????//?4、開始查找,從[查找字符串]中得到第一個字符,標(biāo)記為?first
          ????char?first?=?target[targetOffset];
          ????int?max?=?sourceOffset?+?(sourceCount?-?targetCount);
          ????//?4.1、計算[源字符串最大長度]
          ????for?(int?i?=?sourceOffset?+?fromIndex;?i?<=?max;?i++)?{
          ????????//?4.2.1、從[源字符串]中,查找到第一個匹配到[目標(biāo)字符串]?first?的位置
          ????????//?for循環(huán)中,增加?while?循環(huán)
          ????????/*?Look?for?first?character.?*/
          ????????if?(source[i]?!=?first)?{
          ????????????while?(++i?<=?max?&&?source[i]?!=?first);
          ????????}
          ????????//?4.2.2、如果在[源字符串]中,找到首個[目標(biāo)字符串],
          ????????//?則匹配是否等于[目標(biāo)字符串]
          ????????/*?Found?first?character,?now?look?at?the?rest?of?v2?*/
          ????????if?(i?<=?max)?{
          ????????????//?4.2.2.1、得到下一個要匹配的位置,標(biāo)記為?j
          ????????????int?j?=?i?+?1;
          ????????????//?4.2.2.2、得到其余[目標(biāo)字符串]的長度,標(biāo)記為?end
          ????????????int?end?=?j?+?targetCount?-?1;
          ????????????//?4.2.2.3、遍歷,其余[目標(biāo)字符串],從?k?開始,
          ????????????//?如果 j 不越界(小于 end ,表示:其余[目標(biāo)字符串]的范圍),
          ????????????//?同時[源字符串]==[目標(biāo)字符串],則
          ????????????//?自增,繼續(xù)查找匹配。j++?、 k++
          ????????????for?(int?k?=?targetOffset?+?1;?j?????????????????????==?target[k];?j++,?k++);
          ????????????// 4.2.2.4、如果 j 與 end 相等,則表示:
          ????????????//?源字符串中匹配到目標(biāo)字符串,匹配結(jié)束,返回 i 。
          ????????????if?(j?==?end)?{
          ????????????????/*?Found?whole?string.?*/
          ????????????????return?i?-?sourceOffset;
          ????????????}
          ????????}
          ????}
          ????return?-1;
          }

          這段代碼看起來平平無奇,而且查找的方式和我們上面的優(yōu)化方案非常像,都是先查找首個匹配字符,然后再做循環(huán)查找整個匹配的字符串。

          單純的靠代碼優(yōu)化把耗時從 2ms 縮減到了 0ms ,只能是一個大寫的佩服,不愧是寫 jdk 源碼的大神。


          感謝閱讀



          瀏覽 29
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久精品蜜芽亚洲国产AV | 天天爱天天干天天操 | 欧美靠逼视频 | 99国产精品久久久久久久成人 | 黄色操B 极品无码 |