[{"data":1,"prerenderedAt":2690},["ShallowReactive",2],{"\u002F2026\u002Fkmp-(knuth-morris-pratt)":3,"surround-\u002F2026\u002Fkmp-(knuth-morris-pratt)":2684},{"id":4,"title":5,"body":6,"categories":2619,"date":2621,"description":5,"draft":2622,"extension":2573,"image":2623,"meta":2624,"navigation":2669,"path":2670,"permalink":2671,"published":2671,"readingTime":2672,"recommend":2671,"references":2671,"seo":2677,"sitemap":2678,"stem":2679,"tags":2680,"type":2682,"updated":2621,"__hash__":2683},"content\u002Fposts\u002F2026\u002FKMP-(Knuth-Morris-Pratt).md","KMP-(Knuth-Morris-Pratt)",{"type":7,"value":8,"toc":2577},"minimark",[9,13,28,50,53,56,59,72,110,113,156,162,164,168,368,370,374,381,385,419,422,431,519,526,528,534,540,562,565,583,650,666,668,675,683,686,726,729,737,740,857,859,863,873,970,998,1000,1006,1022,1032,1039,1044,1050,1064,1073,1082,1086,1092,1102,1122,1131,1182,1186,1192,1208,1211,1219,1222,1239,1242,1305,1338,1340,1342,1349,1445,1518,1564,1660,1906,2041,2143,2146,2363,2472,2474,2477,2499,2501,2505,2542,2547,2549,2552,2556,2559,2565,2568],[10,11,12],"h2",{"id":12},"引言",[14,15,18,22,25],"poetry",{"author":16,"title":17},"Maksim","写在前面",[19,20,21],"p",{},"说实话，\nKMP 我已经学过好几次了。",[19,23,24],{},"第一次接触时，\n真的觉得好难啊——\n但最终还是勉强记住了。",[19,26,27],{},"然而每次需要手写时，\n还是会写着写着就懵。",[29,30,33],"alert",{"title":31,"type":32},"这篇文章的目标","info",[19,34,35,36,40,41,46,47],{},"帮你",[37,38,39],"strong",{},"真正理解"," KMP 的思想，并能够亲手实现出来。下次面试遇到，闭着眼也能写。 ",[42,43,45],"badge",{":round":44},"true","C++"," ",[42,48,49],{":round":44},"Java",[51,52],"hr",{},[10,54,55],{"id":55},"问题描述",[19,57,58],{},"我们要解决的问题非常简单——",[60,61,62,63,67,68,71],"quote",{},"在一个字符串 ",[64,65,66],"code",{"code":66},"str"," 里，找到 ",[64,69,70],{"code":70},"match"," 字符串最早出现的位置。如果找到了就返回索引，找不到就返回 -1。",[29,73,76],{"title":74,"type":75},"举个例子","question",[77,78,79,97],"ul",{},[80,81,82,88,89,93,94],"li",{},[64,83,86],{"className":84,"code":86,"language":87},[85],"language-cpp","str = \"abcde\"","cpp","，",[64,90,92],{"className":91,"code":92,"language":87},[85],"match = \"cd\""," → 返回 ",[37,95,96],{},"2",[80,98,99,88,103,93,107],{},[64,100,102],{"className":101,"code":102,"language":87},[85],"str = \"abcd\"",[64,104,106],{"className":105,"code":106,"language":87},[85],"match = \"de\"",[37,108,109],{},"-1",[19,111,112],{},"本文要点如下：",[114,115,116],"card-list",{},[77,117,118,121,124,144,150,153],{},[80,119,120],{},"KMP 算法是干什么的",[80,122,123],{},"先看 BF（Brute Force）暴力解法",[80,125,126,127,130,131],{},"认识 ",[64,128,129],{"code":129},"next"," 数组\n",[77,132,133,138,141],{},[80,134,135,137],{},[64,136,129],{"code":129}," 数组的含义",[80,139,140],{},"如何用它加速匹配",[80,142,143],{},"为什么能加速（反证法）",[80,145,146,147,149],{},"如何快速求出 ",[64,148,129],{"code":129}," 数组",[80,151,152],{},"完整代码实现（C++ & Java）",[80,154,155],{},"时空复杂度分析",[157,158,159],"blockquote",{},[19,160,161],{},"全文主要采用 C++ 叙述，不影响 KMP 算法的掌握。Java 完整代码详见后文。",[51,163],{},[10,165,167],{"id":166},"kmp-是什么","KMP 是什么？",[169,170,171,174,180,183,198,201],"timeline",{},[19,172,173],{},"{1977 年}",[19,175,176,179],{},[37,177,178],{},"Donald Knuth, Vaughan Pratt, James H. Morris"," 三人提出了这个算法",[19,181,182],{},"{命名由来}",[19,184,185,186,189,190,193,194,197],{},"取三人姓氏首字母 → ",[37,187,188],{},"K","nuth-",[37,191,192],{},"M","orris-",[37,195,196],{},"P","ratt",[19,199,200],{},"{核心用途}",[19,202,203,204,300,301],{},"字符串匹配 \u002F 文本搜索，将暴力匹配的 ",[205,206,209,249],"span",{"className":207},[208],"katex",[205,210,213],{"className":211},[212],"katex-mathml",[214,215,217],"math",{"xmlns":216},"http:\u002F\u002Fwww.w3.org\u002F1998\u002FMath\u002FMathML",[218,219,220,244],"semantics",{},[221,222,223,227,232,235,238,241],"mrow",{},[224,225,226],"mi",{},"O",[228,229,231],"mo",{"stretchy":230},"false","(",[224,233,234],{},"n",[228,236,237],{},"×",[224,239,240],{},"m",[228,242,243],{"stretchy":230},")",[245,246,248],"annotation",{"encoding":247},"application\u002Fx-tex","O(n \\times m)",[205,250,253,287],{"className":251,"ariaHidden":44},[252],"katex-html",[205,254,257,262,268,272,275,280,284],{"className":255},[256],"base",[205,258],{"className":259,"style":261},[260],"strut","height:1em;vertical-align:-0.25em;",[205,263,226],{"className":264,"style":267},[265,266],"mord","mathnormal","margin-right:0.0278em;",[205,269,231],{"className":270},[271],"mopen",[205,273,234],{"className":274},[265,266],[205,276],{"className":277,"style":279},[278],"mspace","margin-right:0.2222em;",[205,281,237],{"className":282},[283],"mbin",[205,285],{"className":286,"style":279},[278],[205,288,290,293,296],{"className":289},[256],[205,291],{"className":292,"style":261},[260],[205,294,240],{"className":295},[265,266],[205,297,243],{"className":298},[299],"mclose"," 优化到 ",[205,302,304,329],{"className":303},[208],[205,305,307],{"className":306},[212],[214,308,309],{"xmlns":216},[218,310,311,326],{},[221,312,313,315,317,319,322,324],{},[224,314,226],{},[228,316,231],{"stretchy":230},[224,318,234],{},[228,320,321],{},"+",[224,323,240],{},[228,325,243],{"stretchy":230},[245,327,328],{"encoding":247},"O(n + m)",[205,330,332,356],{"className":331,"ariaHidden":44},[252],[205,333,335,338,341,344,347,350,353],{"className":334},[256],[205,336],{"className":337,"style":261},[260],[205,339,226],{"className":340,"style":267},[265,266],[205,342,231],{"className":343},[271],[205,345,234],{"className":346},[265,266],[205,348],{"className":349,"style":279},[278],[205,351,321],{"className":352},[283],[205,354],{"className":355,"style":279},[278],[205,357,359,362,365],{"className":358},[256],[205,360],{"className":361,"style":261},[260],[205,363,240],{"className":364},[265,266],[205,366,243],{"className":367},[299],[51,369],{},[10,371,373],{"id":372},"先看看-bf-暴力解法","先看看 BF 暴力解法 💥",[19,375,376,377,380],{},"KMP 的核心思想是",[37,378,379],{},"优化暴力匹配","，所以我们先来看看 BF 解法长什么样。",[382,383,384],"h3",{"id":384},"思路",[114,386,387],{},[77,388,389,403,409,416],{},[80,390,391,392,394,395,398,399,402],{},"遍历 ",[64,393,66],{"code":66}," 的所有可能起点（从 ",[64,396,397],{"code":397},"0"," 到 ",[64,400,401],{"code":401},"n - m","）",[80,404,405,406,408],{},"对每个起点，依次匹配 ",[64,407,70],{"code":70}," 的字符",[80,410,411,412,415],{},"如果成功匹配 ",[64,413,414],{"code":414},"match.length()"," 个字符 → 找到了",[80,417,418],{},"如果匹配到一半发现不对 → 换下一个起点继续",[382,420,421],{"id":421},"代码实现",[423,424,429],"pre",{"className":425,"code":426,"filename":427,"language":87,"meta":428},[85],"class Solution {\npublic:\n    int strStr(string haystack, string needle) {\n        int n = haystack.length(), m = needle.length();\n        if (n \u003C m) return -1;\n        for (int i = 0; i \u003C= n - m; i++) {\n            int j;\n            for (j = 0; j \u003C m; j++) {\n                if (haystack[i + j] != needle[j]) {\n                    break;\n                }\n            }\n            if (j == m) return i;\n        }\n        return -1;\n    }\n};\n","BF.cpp","",[64,430,426],{"__ignoreMap":428},[29,432,435,508,516],{"title":433,"type":434},"BF 的死穴","warning",[19,436,437,438,503,504,507],{},"时间复杂度 ",[205,439,441,464],{"className":440},[208],[205,442,444],{"className":443},[212],[214,445,446],{"xmlns":216},[218,447,448,462],{},[221,449,450,452,454,456,458,460],{},[224,451,226],{},[228,453,231],{"stretchy":230},[224,455,234],{},[228,457,237],{},[224,459,240],{},[228,461,243],{"stretchy":230},[245,463,248],{"encoding":247},[205,465,467,491],{"className":466,"ariaHidden":44},[252],[205,468,470,473,476,479,482,485,488],{"className":469},[256],[205,471],{"className":472,"style":261},[260],[205,474,226],{"className":475,"style":267},[265,266],[205,477,231],{"className":478},[271],[205,480,234],{"className":481},[265,266],[205,483],{"className":484,"style":279},[278],[205,486,237],{"className":487},[283],[205,489],{"className":490,"style":279},[278],[205,492,494,497,500],{"className":493},[256],[205,495],{"className":496,"style":261},[260],[205,498,240],{"className":499},[265,266],[205,501,243],{"className":502},[299],"。当出现下面这种情况时，会产生",[37,505,506],{},"大量的无效匹配","：",[423,509,514],{"className":510,"code":512,"language":513},[511],"language-text","str   = aaaaaaaaaaaaaaaaaaaaab\nmatch = aaab\n","text",[64,515,512],{"__ignoreMap":428},[19,517,518],{},"每次匹配到末尾才发现不对，然后起点 +1，再来一遍——效率极低。",[60,520,521,522,525],{},"KMP 的本质，就是",[37,523,524],{},"复用已经匹配过的信息","，不走回头路。",[51,527],{},[10,529,126,531,533],{"id":530},"认识-next-数组",[64,532,129],{"code":129}," 数组 💡",[19,535,536,537,539],{},"KMP 的核心优化点就是 ",[64,538,129],{"code":129}," 数组。但它到底是什么？别急，慢慢来。",[29,541,543,556],{"title":542,"type":32},"next[i] 的定义",[19,544,545,548,549,555],{},[64,546,547],{"code":547},"next[i]"," 表示：",[37,550,551,554],{},[64,552,553],{"code":553},"match[0:i]"," 的前后缀的最大匹配长度","。",[19,557,558,559,555],{},"⚠️ 注意：前后缀",[37,560,561],{},"不能等于这个字符串本身",[382,563,564],{"id":564},"举个例子手算一下",[19,566,567,568,571,572,575,576,579,580,582],{},"假设 ",[64,569,570],{"code":570},"match = \"aabaabtabc\"","，我们来计算 ",[64,573,574],{"code":574},"next[6]","，也就是 ",[64,577,578],{"code":578},"match[0:6] = \"aabaab\""," 的 ",[64,581,129],{"code":129}," 值。",[584,585,587,597],"folding",{"title":586,":open":44},"📐 完整推导过程（左右指针扫描法）",[19,588,589,590,593,594,555],{},"我们要找 ",[37,591,592],{},"前后缀匹配最长的部分","：前缀从开头开始，后缀从结尾往前看，且都不能包含整个 ",[64,595,596],{"code":596},"aabaab",[598,599,601,610,618,626,634,642],"tab",{":tabs":600},"[\"第 1 步\",\"第 2 步\",\"第 3 步 ✅\",\"第 4 步\",\"第 5 步\",\"终止\"]",[602,603,604],"template",{"v-slot:tab1":428},[423,605,608],{"className":606,"code":607,"language":513,"meta":428},[511],"aabaab\nl    r\n前缀: a\n后缀: b\n❌ 不匹配，最大长度暂为 0\n",[64,609,607],{"__ignoreMap":428},[602,611,612],{"v-slot:tab2":428},[423,613,616],{"className":614,"code":615,"language":513,"meta":428},[511],"aabaab\n l  r\n前缀: aa\n后缀: ab\n❌ 不匹配，最大长度暂为 0\n",[64,617,615],{"__ignoreMap":428},[602,619,620],{"v-slot:tab3":428},[423,621,624],{"className":622,"code":623,"language":513,"meta":428},[511],"aabaab\n  lr\n前缀: aab\n后缀: aab\n✅ 匹配！最大长度更新为 3\n",[64,625,623],{"__ignoreMap":428},[602,627,628],{"v-slot:tab4":428},[423,629,632],{"className":630,"code":631,"language":513,"meta":428},[511],"aabaab\n  rl\n前缀: aaba\n后缀: baab\n❌ 不匹配，保持 3\n",[64,633,631],{"__ignoreMap":428},[602,635,636],{"v-slot:tab5":428},[423,637,640],{"className":638,"code":639,"language":513,"meta":428},[511],"aabaab\n r  l\n前缀: aabaa\n后缀: abaab\n❌ 不匹配，保持 3\n",[64,641,639],{"__ignoreMap":428},[602,643,644],{"v-slot:tab6":428},[423,645,648],{"className":646,"code":647,"language":513,"meta":428},[511],"aabaab\nr    l\n前后缀已经等于字符串本身，\n不符合 next 的定义。\n\n最终: next[6] = 3 🎉\n",[64,649,647],{"__ignoreMap":428},[29,651,653],{"title":652},"结论",[19,654,655,657,658,665],{},[64,656,547],{"code":547}," 让我们知道：",[37,659,660,661,664],{},"如果在位置 ",[64,662,663],{"code":663},"i"," 匹配失败，模式串该回退到哪里","。这样就能避免大量无效匹配，显著提高效率。",[51,667],{},[10,669,671,672,674],{"id":670},"如何用-next-数组加速匹配","如何用 ",[64,673,129],{"code":129}," 数组加速匹配",[157,676,677],{},[19,678,679,680,682],{},"暂时不要考虑 ",[64,681,129],{"code":129}," 数组怎么求，先理解它怎么用。",[382,684,685],{"id":685},"核心思想",[114,687,688],{},[77,689,690,700,714],{},[80,691,692,693,696,697],{},"如果 ",[64,694,695],{"code":695},"str[i] == match[j]","：匹配成功，",[64,698,699],{"code":699},"i++, j++",[80,701,692,702,705,706,709,710,713],{},[64,703,704],{"code":704},"str[i] != match[j]"," 且 ",[64,707,708],{"code":708},"j > 0","：跳到 ",[64,711,712],{"code":712},"next[j]"," 位置，避免重新匹配",[80,715,692,716,705,718,721,722,725],{},[64,717,704],{"code":704},[64,719,720],{"code":720},"j == 0","：没法跳了，",[64,723,724],{"code":724},"i++"," 继续",[382,727,421],{"id":728},"代码实现-1",[423,730,735],{"className":731,"code":732,"filename":733,"language":87,"meta":734},[85],"int kmp(const string& str, const string& match) {\n    int n = str.length(), m = match.length();\n    if (n \u003C m) return -1;\n\n    vector\u003Cint> next = next_array(match);\n\n    int i = 0, j = 0;\n    while (i \u003C n && j \u003C m) {\n        if (str[i] == match[j]) {\n            i++, j++;\n        } else if (j > 0) {\n            j = next[j];   \u002F\u002F 匹配失败，回溯到 next[j]\n        } else {\n            i++;           \u002F\u002F 没有可以回溯的地方，向前推进\n        }\n    }\n\n    return (j == m) ? i - j : -1;\n}\n","kmp.cpp","icon=tabler:rocket",[64,736,732],{"__ignoreMap":428},[382,738,739],{"id":739},"模拟跑一遍",[584,741,743,756,762,772,779,809,818,824,844,850],{"title":742,":open":44},"🎬 一个完整的匹配过程演示",[19,744,745,746,748,749,751,752,755],{},"看下面这个例子。",[64,747,66],{"code":66}," 和 ",[64,750,70],{"code":70}," 在 ",[64,753,754],{"code":754},"index = 13"," 处第一次失配：",[423,757,760],{"className":758,"code":759,"language":513,"meta":428},[511],"index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13\nstr  = a a b a a b c a a b a  a  b  a ...\nmatch= a a b a a b c a a b a  a  b  t\nnext =-1 0 1 0 1 2 3 0 1 2 3  4  5  6\n                                    ↑\n                                  i, j\n",[64,761,759],{"__ignoreMap":428},[19,763,764,765,767,768,771],{},"按 BF，下一步应该把起点从 ",[64,766,397],{"code":397}," 挪到 ",[64,769,770],{"code":770},"1","，重新比对——但这是浪费！",[19,773,774,775,778],{},"KMP 看到 ",[64,776,777],{"code":777},"next[13] = 6","，意识到一件事：",[157,780,781,794],{},[19,782,783,786,787,790,791,793],{},[64,784,785],{"code":785},"match[0:6]"," 与 ",[64,788,789],{"code":789},"match[7:13]"," 是完全相同的（这就是 ",[64,792,129],{"code":129}," 的定义）。",[19,795,796,797,799,800,803,804,806,807,555],{},"而 ",[64,798,789],{"code":789}," 已经和 ",[64,801,802],{"code":802},"str[7:13]"," 匹配过了 → ",[64,805,785],{"code":785}," 自动等于 ",[64,808,802],{"code":802},[19,810,811,812,814,815,817],{},"所以可以直接平移，把 ",[64,813,70],{"code":70}," 的起点对齐到 ",[64,816,66],{"code":66}," 的 7：",[423,819,822],{"className":820,"code":821,"language":513,"meta":428},[511],"index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13\nstr  = a a b a a b c a a b a  a  b  a ...\nmatch=               a a b a a b  c  a a b a a b t\n                                    ↑\n                                    i (j 跳到 6)\n",[64,823,821],{"__ignoreMap":428},[19,825,826,827,830,831,748,834,555,837,840,841,507],{},"直接从 ",[64,828,829],{"code":829},"j = 6"," 处开始比对 ",[64,832,833],{"code":833},"str[13]",[64,835,836],{"code":836},"match[6]",[64,838,839],{"code":839},"a != c","，再跳一次 ",[64,842,843],{"code":843},"j = next[6] = 3",[423,845,848],{"className":846,"code":847,"language":513,"meta":428},[511],"index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13\nstr  = a a b a a b c a a b a  a  b  a ...\nmatch=                     a a b a  a  b  c a a b a a b t\n                                    ↑\n                                    i (j 跳到 3)\n",[64,849,847],{"__ignoreMap":428},[19,851,852,853,856],{},"终于！",[64,854,855],{"code":855},"str[13] == match[3]","，匹配上了，继续推进。",[51,858],{},[382,860,862],{"id":861},"为什么这样能加速反证一下","为什么这样能加速？反证一下",[29,864,866],{"title":865,"type":75},"灵魂拷问",[19,867,868,869,872],{},"凭什么可以直接跳过中间那些起点？万一从 ",[64,870,871],{"code":871},"start + 1"," 开始就能匹配上呢？",[584,874,876,886,892,906,919,934,940,957],{"title":875},"🧠 反证法：跳过的位置一定不会匹配成功",[19,877,878,879,882,883,555],{},"设 ",[64,880,881],{"code":881},"next[j] = k","，则有 ",[64,884,885],{"code":885},"match[0, k) == match[j-k, j)",[423,887,890],{"className":888,"code":889,"language":513,"meta":428},[511],"      start   x   start+k   end-k   end\n        |     |     |         |      |\nstr  : -----------------------------l\nmatch: -----------------------------y\nnext :-1 0----------------------- ---k\n                                     ↑\n                                   i, j\n",[64,891,889],{"__ignoreMap":428},[19,893,894,895,898,899,902,903,555],{},"假设存在某个起点 ",[64,896,897],{"code":897},"x ∈ [start+1, end-k)","，使得 ",[64,900,901],{"code":901},"str[x : end]"," 能匹配 ",[64,904,905],{"code":905},"match[0 : end - x]",[19,907,908,909,911,912,915,916,555],{},"那么这意味着 ",[64,910,905],{"code":905}," 是 ",[64,913,914],{"code":914},"str[start : end]"," 的一个后缀，长度 ",[64,917,918],{"code":918},"end - x",[19,920,921,922,924,925,928,929,911,931,933],{},"由于 ",[64,923,914],{"code":914}," 已经匹配了 ",[64,926,927],{"code":927},"match[0 : j]","，这等价于：",[64,930,905],{"code":905},[64,932,927],{"code":927}," 的一个真后缀，且也是真前缀。",[19,935,936,937,555],{},"→ 这意味着 ",[64,938,939],{"code":939},"next[j] >= end - x",[19,941,942,943,945,946,949,950,953,954,956],{},"但 ",[64,944,897],{"code":897},"，所以 ",[64,947,948],{"code":948},"end - x > k","，即 ",[64,951,952],{"code":952},"next[j] > k","，与 ",[64,955,881],{"code":881}," 矛盾！",[19,958,959,960,555,963,969],{},"∴ 不存在这样的 ",[64,961,962],{"code":962},"x",[37,964,965,968],{},[64,966,967],{"code":967},"[start+1, end-k)"," 之间的起点全部可以安全跳过。"," ∎",[29,971,973],{"title":972,"type":32},"一句话总结加速原理",[974,975,976,990],"ol",{},[80,977,978,149,980,46,983,786,986,989],{},[64,979,129],{"code":129},[37,981,982],{},"保证了",[64,984,985],{"code":985},"[start, start+k)",[64,987,988],{"code":988},"[end-k, end)"," 必然相等，无需重新比对。",[80,991,992,46,995,997],{},[37,993,994],{},"反证法证明了",[64,996,967],{"code":967}," 之间的起点全是死路，可以放心跳过。",[51,999],{},[10,1001,1003,1004,149],{"id":1002},"如何快速求-next-数组","如何快速求 ",[64,1005,129],{"code":129},[19,1007,1008,1009,1011,1012,1014,1015,1018,1019,555],{},"求 ",[64,1010,129],{"code":129}," 数组的过程，本质上和上面\"用 ",[64,1013,129],{"code":129}," 加速匹配\"的思想",[37,1016,1017],{},"完全一样","——核心都是",[37,1020,1021],{},"跳转复用",[382,1023,1025,1026,1029,1030],{"id":1024},"已知-next0i-1求-nexti","已知 ",[64,1027,1028],{"code":1028},"next[0..i-1]","，求 ",[64,1031,547],{"code":547},[19,1033,1034,1035,1038],{},"边界：",[64,1036,1037],{"code":1037},"next[0] = -1, next[1] = 0","，这两个直接钦定。",[1040,1041,1043],"h4",{"id":1042},"情形一直接续上","情形一：直接续上 ✨",[423,1045,1048],{"className":1046,"code":1047,"language":513,"meta":428},[511],"match = a b a t a b a s a b a t a b a s ?\nnext  = - - - - - - - - - - - - - - - 7 ?\n                       ↑               ↑\n                       7               i\n",[64,1049,1047],{"__ignoreMap":428},[19,1051,1025,1052,1055,1056,1059,1060,1063],{},[64,1053,1054],{"code":1054},"next[i-1] = 7","，意味着 ",[64,1057,1058],{"code":1058},"match[i-1]"," 之前的前后缀匹配长度是 7（",[64,1061,1062],{"code":1062},"abataba","）。",[19,1065,1066,1067,786,1069,1072],{},"只需要看 ",[64,1068,1058],{"code":1058},[64,1070,1071],{"code":1071},"match[7]"," 是否相等：",[77,1074,1075],{},[80,1076,1077,1078,1081],{},"相等 → ",[64,1079,1080],{"code":1080},"next[i] = next[i-1] + 1 = 8","，皆大欢喜。",[1040,1083,1085],{"id":1084},"情形二跳一次再续上","情形二：跳一次再续上 🎯",[423,1087,1090],{"className":1088,"code":1089,"language":513,"meta":428},[511],"match = a b a t a b a s a b a t a b a t ?\nnext  = - - - - - - - - - - - - - - - 7 ?\n            ↑          ↑               ↑\n            3          7               i\n",[64,1091,1089],{"__ignoreMap":428},[19,1093,1094,1095,953,1098,1101],{},"这次 ",[64,1096,1097],{"code":1097},"match[i-1] = t",[64,1099,1100],{"code":1100},"match[7] = s"," 不相等。",[19,1103,1104,1105,1107,1108,46,1111,1114,1115,1118,1119,555],{},"直觉：",[64,1106,547],{"code":547}," 一定 ",[37,1109,1110],{},"小于",[64,1112,1113],{"code":1113},"next[i-1]","。继续往前跳——",[64,1116,1117],{"code":1117},"next[7] = 3","，看 ",[64,1120,1121],{"code":1121},"match[3]",[19,1123,692,1124,1127,1128,555],{},[64,1125,1126],{"code":1126},"match[3] == match[i-1]","，则 ",[64,1129,1130],{"code":1130},"next[i] = next[3] + 1 = 4",[584,1132,1134,1137,1170],{"title":1133},"🤔 为什么这样跳是对的？",[19,1135,1136],{},"关键在于\"前后缀的对应\"：",[77,1138,1139,1151,1161],{},[80,1140,1141,1143,1144,1147,1148],{},[64,1142,1054],{"code":1054}," 意味着 ",[64,1145,1146],{"code":1146},"match[0:7]"," == ",[64,1149,1150],{"code":1150},"match[i-8:i-1]",[80,1152,1153,1143,1155,1147,1158],{},[64,1154,1117],{"code":1117},[64,1156,1157],{"code":1157},"match[0:3]",[64,1159,1160],{"code":1160},"match[4:7]",[80,1162,1163,1164,1166,1167],{},"由对应关系传递：",[64,1165,1157],{"code":1157}," 也等于 ",[64,1168,1169],{"code":1169},"match[i-4:i-1]",[19,1171,1172,1173,88,1175,1178,1179,1181],{},"所以只要 ",[64,1174,1126],{"code":1126},[64,1176,1177],{"code":1177},"abat"," 就成了 ",[64,1180,553],{"code":553}," 的一个新的最长公共前后缀。",[1040,1183,1185],{"id":1184},"情形三跳到头还不行","情形三：跳到头还不行 🛑",[423,1187,1190],{"className":1188,"code":1189,"language":513,"meta":428},[511],"match = a b a\nnext  = - 1 0 ?\n              ↑\n              i\n",[64,1191,1189],{"__ignoreMap":428},[19,1193,1194,1197,1198,1200,1201,1204,1205,555],{},[64,1195,1196],{"code":1196},"b"," 字符 ",[64,1199,129],{"code":129}," 值为 0，跳到 0 下标处。",[64,1202,1203],{"code":1203},"a != b","，已经无路可跳，直接确定 ",[64,1206,1207],{"code":1207},"next[i] = 0",[382,1209,421],{"id":1210},"代码实现-2",[423,1212,1217],{"className":1213,"code":1214,"filename":1215,"language":87,"meta":1216},[85],"vector\u003Cint> next_array(const string& s, int n) {\n    if (n == 1) return {-1};\n    vector\u003Cint> next(n);\n    next[0] = -1, next[1] = 0;\n    for (int i = 2, pre = 0; i \u003C n;) {\n        if (s[i - 1] == s[pre])  next[i++] = ++pre;\n        else if (pre > 0)        pre = next[pre];\n        else                     next[i++] = 0;\n    }\n    return next;\n}\n","next_array.cpp","icon=tabler:list-numbers",[64,1218,1214],{"__ignoreMap":428},[382,1220,1221],{"id":1221},"代码逐行解释",[29,1223,1225],{"title":1224,"type":32},"变量含义",[77,1226,1227,1234],{},[80,1228,1229,1231,1232],{},[64,1230,663],{"code":663},"：当前要求解的下标，即正在求 ",[64,1233,547],{"code":547},[80,1235,1236,1238],{},[64,1237,423],{"code":423},"：前一个用于比对的下标（情形一里的\"7\"，情形二跳转后的\"3\"）",[19,1240,1241],{},"三个分支对应三种情形：",[114,1243,1244],{},[77,1245,1246,1274,1293],{},[80,1247,1248,46,1251,1254,1255,1258],{},[37,1249,1250],{},"分支 1",[64,1252,1253],{"code":1253},"s[i-1] == s[pre]","：续上！",[64,1256,1257],{"code":1257},"next[i++] = ++pre",[77,1259,1260,1268],{},[80,1261,1262,1263,507,1265],{},"前增 ",[64,1264,423],{"code":423},[64,1266,1267],{"code":1267},"next[i] = pre + 1",[80,1269,1270,1271,1273],{},"后增 ",[64,1272,663],{"code":663},"：当前位置结算完毕，处理下一个",[80,1275,1276,46,1279,1282,1283,1286],{},[37,1277,1278],{},"分支 2",[64,1280,1281],{"code":1281},"pre > 0","：跳一次！",[64,1284,1285],{"code":1285},"pre = next[pre]",[77,1287,1288],{},[80,1289,1290,1292],{},[64,1291,663],{"code":663}," 不变，下一轮继续比对",[80,1294,1295,46,1298,1301,1302],{},[37,1296,1297],{},"分支 3",[64,1299,1300],{"code":1300},"pre == 0","：到头了！",[64,1303,1304],{"code":1304},"next[i++] = 0",[29,1306,1308,1317],{"title":1307,"type":434},"易错点",[19,1309,1310,1312,1313,1316],{},[64,1311,1257],{"code":1257}," 中的",[37,1314,1315],{},"前增和后增","不能混淆：",[77,1318,1319,1330],{},[80,1320,1321,1324,1325,1327,1328],{},[64,1322,1323],{"code":1323},"++pre"," 必须前增 → 因为 ",[64,1326,547],{"code":547}," 要等于新的 ",[64,1329,423],{"code":423},[80,1331,1332,1334,1335,1337],{},[64,1333,724],{"code":724}," 必须后增 → 因为当前轮次还要用 ",[64,1336,663],{"code":663}," 这个值",[51,1339],{},[10,1341,155],{"id":155},[382,1343,1345,1348],{"id":1344},"next_array-的时间复杂度推导",[64,1346,1347],{"code":1347},"next_array"," 的时间复杂度推导",[19,1350,1351,1352,1354,1355,1444],{},"很多人疑惑：第二个分支 ",[64,1353,1285],{"code":1285}," 不是回溯吗？会不会变成 ",[205,1356,1358,1384],{"className":1357},[208],[205,1359,1361],{"className":1360},[212],[214,1362,1363],{"xmlns":216},[218,1364,1365,1381],{},[221,1366,1367,1369,1371,1379],{},[224,1368,226],{},[228,1370,231],{"stretchy":230},[1372,1373,1374,1376],"msup",{},[224,1375,240],{},[1377,1378,96],"mn",{},[228,1380,243],{"stretchy":230},[245,1382,1383],{"encoding":247},"O(m^2)",[205,1385,1387],{"className":1386,"ariaHidden":44},[252],[205,1388,1390,1394,1397,1400,1441],{"className":1389},[256],[205,1391],{"className":1392,"style":1393},[260],"height:1.0641em;vertical-align:-0.25em;",[205,1395,226],{"className":1396,"style":267},[265,266],[205,1398,231],{"className":1399},[271],[205,1401,1403,1406],{"className":1402},[265],[205,1404,240],{"className":1405},[265,266],[205,1407,1410],{"className":1408},[1409],"msupsub",[205,1411,1414],{"className":1412},[1413],"vlist-t",[205,1415,1418],{"className":1416},[1417],"vlist-r",[205,1419,1423],{"className":1420,"style":1422},[1421],"vlist","height:0.8141em;",[205,1424,1426,1431],{"style":1425},"top:-3.063em;margin-right:0.05em;",[205,1427],{"className":1428,"style":1430},[1429],"pstrut","height:2.7em;",[205,1432,1438],{"className":1433},[1434,1435,1436,1437],"sizing","reset-size6","size3","mtight",[205,1439,96],{"className":1440},[265,1437],[205,1442,243],{"className":1443},[299],"？",[29,1446,1448],{"title":1447,"type":75},"均摊分析的小技巧",[19,1449,1450,1451,1517],{},"构造一个变量 ",[205,1452,1454,1479],{"className":1453},[208],[205,1455,1457],{"className":1456},[212],[214,1458,1459],{"xmlns":216},[218,1460,1461,1476],{},[221,1462,1463,1465,1468,1470,1473],{},[224,1464,663],{},[228,1466,1467],{},"−",[224,1469,19],{},[224,1471,1472],{},"r",[224,1474,1475],{},"e",[245,1477,1478],{"encoding":247},"i - pre",[205,1480,1482,1501],{"className":1481,"ariaHidden":44},[252],[205,1483,1485,1489,1492,1495,1498],{"className":1484},[256],[205,1486],{"className":1487,"style":1488},[260],"height:0.7429em;vertical-align:-0.0833em;",[205,1490,663],{"className":1491},[265,266],[205,1493],{"className":1494,"style":279},[278],[205,1496,1467],{"className":1497},[283],[205,1499],{"className":1500,"style":279},[278],[205,1502,1504,1508,1511,1514],{"className":1503},[256],[205,1505],{"className":1506,"style":1507},[260],"height:0.625em;vertical-align:-0.1944em;",[205,1509,19],{"className":1510},[265,266],[205,1512,1472],{"className":1513,"style":267},[265,266],[205,1515,1475],{"className":1516},[265,266],"，观察它在三个分支下如何变化：",[114,1519,1520],{},[77,1521,1522,1535,1550],{},[80,1523,1524,507,1526,1529,1530,46,1532],{},[37,1525,1250],{},[64,1527,1528],{"code":1528},"i++, pre++"," → ",[64,1531,1478],{"code":1478},[37,1533,1534],{},"不变",[80,1536,1537,507,1539,1541,1542,1544,1545,46,1547],{},[37,1538,1278],{},[64,1540,663],{"code":663}," 不变，",[64,1543,423],{"code":423}," 减小 → ",[64,1546,1478],{"code":1478},[37,1548,1549],{},"增加",[80,1551,1552,507,1554,1556,1557,1559,1560,46,1562],{},[37,1553,1297],{},[64,1555,724],{"code":724},", ",[64,1558,423],{"code":423}," 不变 → ",[64,1561,1478],{"code":1478},[37,1563,1549],{},[19,1565,1566,1567,507],{},"观察 ",[205,1568,1570,1600],{"className":1569},[208],[205,1571,1573],{"className":1572},[212],[214,1574,1575],{"xmlns":216},[218,1576,1577,1597],{},[221,1578,1579,1581,1583,1585,1587,1589,1591,1593,1595],{},[224,1580,663],{},[228,1582,321],{},[228,1584,231],{"stretchy":230},[224,1586,663],{},[228,1588,1467],{},[224,1590,19],{},[224,1592,1472],{},[224,1594,1475],{},[228,1596,243],{"stretchy":230},[245,1598,1599],{"encoding":247},"i + (i - pre)",[205,1601,1603,1621,1642],{"className":1602,"ariaHidden":44},[252],[205,1604,1606,1609,1612,1615,1618],{"className":1605},[256],[205,1607],{"className":1608,"style":1488},[260],[205,1610,663],{"className":1611},[265,266],[205,1613],{"className":1614,"style":279},[278],[205,1616,321],{"className":1617},[283],[205,1619],{"className":1620,"style":279},[278],[205,1622,1624,1627,1630,1633,1636,1639],{"className":1623},[256],[205,1625],{"className":1626,"style":261},[260],[205,1628,231],{"className":1629},[271],[205,1631,663],{"className":1632},[265,266],[205,1634],{"className":1635,"style":279},[278],[205,1637,1467],{"className":1638},[283],[205,1640],{"className":1641,"style":279},[278],[205,1643,1645,1648,1651,1654,1657],{"className":1644},[256],[205,1646],{"className":1647,"style":261},[260],[205,1649,19],{"className":1650},[265,266],[205,1652,1472],{"className":1653,"style":267},[265,266],[205,1655,1475],{"className":1656},[265,266],[205,1658,243],{"className":1659},[299],[1661,1662,1663,1861],"table",{},[1664,1665,1666],"thead",{},[1667,1668,1669,1673,1705,1767],"tr",{},[1670,1671,1672],"th",{},"分支",[1670,1674,1676],{"align":1675},"center",[205,1677,1679,1692],{"className":1678},[208],[205,1680,1682],{"className":1681},[212],[214,1683,1684],{"xmlns":216},[218,1685,1686,1690],{},[221,1687,1688],{},[224,1689,663],{},[245,1691,663],{"encoding":247},[205,1693,1695],{"className":1694,"ariaHidden":44},[252],[205,1696,1698,1702],{"className":1697},[256],[205,1699],{"className":1700,"style":1701},[260],"height:0.6595em;",[205,1703,663],{"className":1704},[265,266],[1670,1706,1707],{"align":1675},[205,1708,1710,1731],{"className":1709},[208],[205,1711,1713],{"className":1712},[212],[214,1714,1715],{"xmlns":216},[218,1716,1717,1729],{},[221,1718,1719,1721,1723,1725,1727],{},[224,1720,663],{},[228,1722,1467],{},[224,1724,19],{},[224,1726,1472],{},[224,1728,1475],{},[245,1730,1478],{"encoding":247},[205,1732,1734,1752],{"className":1733,"ariaHidden":44},[252],[205,1735,1737,1740,1743,1746,1749],{"className":1736},[256],[205,1738],{"className":1739,"style":1488},[260],[205,1741,663],{"className":1742},[265,266],[205,1744],{"className":1745,"style":279},[278],[205,1747,1467],{"className":1748},[283],[205,1750],{"className":1751,"style":279},[278],[205,1753,1755,1758,1761,1764],{"className":1754},[256],[205,1756],{"className":1757,"style":1507},[260],[205,1759,19],{"className":1760},[265,266],[205,1762,1472],{"className":1763,"style":267},[265,266],[205,1765,1475],{"className":1766},[265,266],[1670,1768,1769],{"align":1675},[205,1770,1772,1801],{"className":1771},[208],[205,1773,1775],{"className":1774},[212],[214,1776,1777],{"xmlns":216},[218,1778,1779,1799],{},[221,1780,1781,1783,1785,1787,1789,1791,1793,1795,1797],{},[224,1782,663],{},[228,1784,321],{},[228,1786,231],{"stretchy":230},[224,1788,663],{},[228,1790,1467],{},[224,1792,19],{},[224,1794,1472],{},[224,1796,1475],{},[228,1798,243],{"stretchy":230},[245,1800,1599],{"encoding":247},[205,1802,1804,1822,1843],{"className":1803,"ariaHidden":44},[252],[205,1805,1807,1810,1813,1816,1819],{"className":1806},[256],[205,1808],{"className":1809,"style":1488},[260],[205,1811,663],{"className":1812},[265,266],[205,1814],{"className":1815,"style":279},[278],[205,1817,321],{"className":1818},[283],[205,1820],{"className":1821,"style":279},[278],[205,1823,1825,1828,1831,1834,1837,1840],{"className":1824},[256],[205,1826],{"className":1827,"style":261},[260],[205,1829,231],{"className":1830},[271],[205,1832,663],{"className":1833},[265,266],[205,1835],{"className":1836,"style":279},[278],[205,1838,1467],{"className":1839},[283],[205,1841],{"className":1842,"style":279},[278],[205,1844,1846,1849,1852,1855,1858],{"className":1845},[256],[205,1847],{"className":1848,"style":261},[260],[205,1850,19],{"className":1851},[265,266],[205,1853,1472],{"className":1854,"style":267},[265,266],[205,1856,1475],{"className":1857},[265,266],[205,1859,243],{"className":1860},[299],[1862,1863,1864,1878,1894],"tbody",{},[1667,1865,1866,1869,1872,1874],{},[1867,1868,1250],"td",{},[1867,1870,1871],{"align":1675},"+1",[1867,1873,397],{"align":1675},[1867,1875,1876],{"align":1675},[37,1877,1871],{},[1667,1879,1880,1882,1884,1889],{},[1867,1881,1278],{},[1867,1883,397],{"align":1675},[1867,1885,1886,1887,243],{"align":1675},"+(原 pre - next",[205,1888,423],{},[1867,1890,1891],{"align":1675},[37,1892,1893],{},"+正数",[1667,1895,1896,1898,1900,1902],{},[1867,1897,1297],{},[1867,1899,1871],{"align":1675},[1867,1901,397],{"align":1675},[1867,1903,1904],{"align":1675},[37,1905,1871],{},[19,1907,1908,1909,2001,2002,2005,2006,555],{},"→ ",[205,1910,1912,1941],{"className":1911},[208],[205,1913,1915],{"className":1914},[212],[214,1916,1917],{"xmlns":216},[218,1918,1919,1939],{},[221,1920,1921,1923,1925,1927,1929,1931,1933,1935,1937],{},[224,1922,663],{},[228,1924,321],{},[228,1926,231],{"stretchy":230},[224,1928,663],{},[228,1930,1467],{},[224,1932,19],{},[224,1934,1472],{},[224,1936,1475],{},[228,1938,243],{"stretchy":230},[245,1940,1599],{"encoding":247},[205,1942,1944,1962,1983],{"className":1943,"ariaHidden":44},[252],[205,1945,1947,1950,1953,1956,1959],{"className":1946},[256],[205,1948],{"className":1949,"style":1488},[260],[205,1951,663],{"className":1952},[265,266],[205,1954],{"className":1955,"style":279},[278],[205,1957,321],{"className":1958},[283],[205,1960],{"className":1961,"style":279},[278],[205,1963,1965,1968,1971,1974,1977,1980],{"className":1964},[256],[205,1966],{"className":1967,"style":261},[260],[205,1969,231],{"className":1970},[271],[205,1972,663],{"className":1973},[265,266],[205,1975],{"className":1976,"style":279},[278],[205,1978,1467],{"className":1979},[283],[205,1981],{"className":1982,"style":279},[278],[205,1984,1986,1989,1992,1995,1998],{"className":1985},[256],[205,1987],{"className":1988,"style":261},[260],[205,1990,19],{"className":1991},[265,266],[205,1993,1472],{"className":1994,"style":267},[265,266],[205,1996,1475],{"className":1997},[265,266],[205,1999,243],{"className":2000},[299]," 是",[37,2003,2004],{},"严格递增","的，且上界为 ",[205,2007,2009,2025],{"className":2008},[208],[205,2010,2012],{"className":2011},[212],[214,2013,2014],{"xmlns":216},[218,2015,2016,2022],{},[221,2017,2018,2020],{},[1377,2019,96],{},[224,2021,240],{},[245,2023,2024],{"encoding":247},"2m",[205,2026,2028],{"className":2027,"ariaHidden":44},[252],[205,2029,2031,2035,2038],{"className":2030},[256],[205,2032],{"className":2033,"style":2034},[260],"height:0.6444em;",[205,2036,96],{"className":2037},[265],[205,2039,240],{"className":2040},[265,266],[19,2042,2043,2044,88,2096,2142],{},"所以循环总次数 ",[205,2045,2047,2066],{"className":2046},[208],[205,2048,2050],{"className":2049},[212],[214,2051,2052],{"xmlns":216},[218,2053,2054,2063],{},[221,2055,2056,2059,2061],{},[228,2057,2058],{},"≤",[1377,2060,96],{},[224,2062,240],{},[245,2064,2065],{"encoding":247},"\\le 2m",[205,2067,2069,2084],{"className":2068,"ariaHidden":44},[252],[205,2070,2072,2076,2080],{"className":2071},[256],[205,2073],{"className":2074,"style":2075},[260],"height:0.7719em;vertical-align:-0.136em;",[205,2077,2058],{"className":2078},[2079],"mrel",[205,2081],{"className":2082,"style":2083},[278],"margin-right:0.2778em;",[205,2085,2087,2090,2093],{"className":2086},[256],[205,2088],{"className":2089,"style":2034},[260],[205,2091,96],{"className":2092},[265],[205,2094,240],{"className":2095},[265,266],[37,2097,437,2098],{},[205,2099,2101,2121],{"className":2100},[208],[205,2102,2104],{"className":2103},[212],[214,2105,2106],{"xmlns":216},[218,2107,2108,2118],{},[221,2109,2110,2112,2114,2116],{},[224,2111,226],{},[228,2113,231],{"stretchy":230},[224,2115,240],{},[228,2117,243],{"stretchy":230},[245,2119,2120],{"encoding":247},"O(m)",[205,2122,2124],{"className":2123,"ariaHidden":44},[252],[205,2125,2127,2130,2133,2136,2139],{"className":2126},[256],[205,2128],{"className":2129,"style":261},[260],[205,2131,226],{"className":2132,"style":267},[265,266],[205,2134,231],{"className":2135},[271],[205,2137,240],{"className":2138},[265,266],[205,2140,243],{"className":2141},[299],"。✅",[382,2144,2145],{"id":2145},"总复杂度",[205,2147,2150],{"className":2148},[2149],"katex-display",[205,2151,2153,2217],{"className":2152},[208],[205,2154,2156],{"className":2155},[212],[214,2157,2159],{"xmlns":216,"display":2158},"block",[218,2160,2161,2214],{},[221,2162,2163],{},[2164,2165,2167],"menclose",{"notation":2166},"box",[2168,2169,2170],"mstyle",{"scriptlevel":397,"displaystyle":230},[2168,2171,2172],{"scriptlevel":397,"displaystyle":230},[2168,2173,2174],{"scriptlevel":397,"displaystyle":44},[221,2175,2176,2180,2183,2185,2187,2189,2191,2193,2195,2198,2201,2204,2206,2208,2210,2212],{},[2177,2178,2179],"mtext",{},"时间复杂度",[228,2181,2182],{},"=",[224,2184,226],{},[228,2186,231],{"stretchy":230},[224,2188,234],{},[228,2190,321],{},[224,2192,240],{},[228,2194,243],{"stretchy":230},[228,2196,2197],{"separator":44},",",[278,2199],{"width":2200},"1em",[2177,2202,2203],{},"空间复杂度",[228,2205,2182],{},[224,2207,226],{},[228,2209,231],{"stretchy":230},[224,2211,240],{},[228,2213,243],{"stretchy":230},[245,2215,2216],{"encoding":247},"\\boxed{\\text{时间复杂度} = O(n + m), \\quad \\text{空间复杂度} = O(m)}",[205,2218,2220],{"className":2219,"ariaHidden":44},[252],[205,2221,2223,2227],{"className":2222},[256],[205,2224],{"className":2225,"style":2226},[260],"height:1.68em;vertical-align:-0.59em;",[205,2228,2230],{"className":2229},[265],[205,2231,2234,2354],{"className":2232},[1413,2233],"vlist-t2",[205,2235,2237,2349],{"className":2236},[1417],[205,2238,2241,2337],{"className":2239,"style":2240},[1421],"height:1.09em;",[205,2242,2244,2248],{"style":2243},"top:-3.68em;",[205,2245],{"className":2246,"style":2247},[1429],"height:3.68em;",[205,2249,2252],{"className":2250},[2251],"boxpad",[205,2253,2255],{"className":2254},[265],[205,2256,2258,2265,2268,2271,2274,2277,2280,2283,2286,2289,2292,2295,2298,2302,2306,2310,2316,2319,2322,2325,2328,2331,2334],{"className":2257},[265],[205,2259,2261],{"className":2260},[265,513],[205,2262,2179],{"className":2263},[265,2264],"cjk_fallback",[205,2266],{"className":2267,"style":2083},[278],[205,2269,2182],{"className":2270},[2079],[205,2272],{"className":2273,"style":2083},[278],[205,2275,226],{"className":2276,"style":267},[265,266],[205,2278,231],{"className":2279},[271],[205,2281,234],{"className":2282},[265,266],[205,2284],{"className":2285,"style":279},[278],[205,2287,321],{"className":2288},[283],[205,2290],{"className":2291,"style":279},[278],[205,2293,240],{"className":2294},[265,266],[205,2296,243],{"className":2297},[299],[205,2299,2197],{"className":2300},[2301],"mpunct",[205,2303],{"className":2304,"style":2305},[278],"margin-right:1em;",[205,2307],{"className":2308,"style":2309},[278],"margin-right:0.1667em;",[205,2311,2313],{"className":2312},[265,513],[205,2314,2203],{"className":2315},[265,2264],[205,2317],{"className":2318,"style":2083},[278],[205,2320,2182],{"className":2321},[2079],[205,2323],{"className":2324,"style":2083},[278],[205,2326,226],{"className":2327,"style":267},[265,266],[205,2329,231],{"className":2330},[271],[205,2332,240],{"className":2333},[265,266],[205,2335,243],{"className":2336},[299],[205,2338,2340,2343],{"style":2339},"top:-3.09em;",[205,2341],{"className":2342,"style":2247},[1429],[205,2344],{"className":2345,"style":2348},[2346,2347],"stretchy","fbox","height:1.68em;border-style:solid;border-width:0.04em;",[205,2350,2353],{"className":2351},[2352],"vlist-s","​",[205,2355,2357],{"className":2356},[1417],[205,2358,2361],{"className":2359,"style":2360},[1421],"height:0.59em;",[205,2362],{},[19,2364,2365,2366,2368,2369,949,2371,2425,2426,555],{},"一般 ",[64,2367,66],{"code":66}," 长度远大于 ",[64,2370,70],{"code":70},[205,2372,2374,2393],{"className":2373},[208],[205,2375,2377],{"className":2376},[212],[214,2378,2379],{"xmlns":216},[218,2380,2381,2390],{},[221,2382,2383,2385,2388],{},[224,2384,234],{},[228,2386,2387],{},"≫",[224,2389,240],{},[245,2391,2392],{"encoding":247},"n \\gg m",[205,2394,2396,2415],{"className":2395,"ariaHidden":44},[252],[205,2397,2399,2403,2406,2409,2412],{"className":2398},[256],[205,2400],{"className":2401,"style":2402},[260],"height:0.5782em;vertical-align:-0.0391em;",[205,2404,234],{"className":2405},[265,266],[205,2407],{"className":2408,"style":2083},[278],[205,2410,2387],{"className":2411},[2079],[205,2413],{"className":2414,"style":2083},[278],[205,2416,2418,2422],{"className":2417},[256],[205,2419],{"className":2420,"style":2421},[260],"height:0.4306em;",[205,2423,240],{"className":2424},[265,266],"，所以可以近似为 ",[37,2427,2428],{},[205,2429,2431,2451],{"className":2430},[208],[205,2432,2434],{"className":2433},[212],[214,2435,2436],{"xmlns":216},[218,2437,2438,2448],{},[221,2439,2440,2442,2444,2446],{},[224,2441,226],{},[228,2443,231],{"stretchy":230},[224,2445,234],{},[228,2447,243],{"stretchy":230},[245,2449,2450],{"encoding":247},"O(n)",[205,2452,2454],{"className":2453,"ariaHidden":44},[252],[205,2455,2457,2460,2463,2466,2469],{"className":2456},[256],[205,2458],{"className":2459,"style":261},[260],[205,2461,226],{"className":2462,"style":267},[265,266],[205,2464,231],{"className":2465},[271],[205,2467,234],{"className":2468},[265,266],[205,2470,243],{"className":2471},[299],[51,2473],{},[10,2475,2476],{"id":2476},"完整代码实现",[598,2478,2480,2488],{":tabs":2479},"[\"C++\", \"Java\"]",[602,2481,2482],{"v-slot:tab1":428},[423,2483,2486],{"className":2484,"code":2485,"filename":733,"language":87,"meta":428},[85],"#include \u003Ciostream>\n#include \u003Cvector>\n#include \u003Cstring>\nusing namespace std;\n\nint kmp(const string& str, const string& match);\nvector\u003Cint> next_array(const string& s, int n);\n\nint kmp(const string& str, const string& match) {\n    int n = str.length(), m = match.length();\n    vector\u003Cint> next = next_array(match, m);\n    int i = 0, j = 0;\n    while (i \u003C n && j \u003C m) {\n        if (str[i] == match[j]) ++i, ++j;\n        else if (j == 0) ++i;\n        else j = next[j];\n    }\n    return j == m ? i - j : -1;\n}\n\nvector\u003Cint> next_array(const string& s, int n) {\n    if (n == 1) return {-1};\n    vector\u003Cint> next(n);\n    next[0] = -1, next[1] = 0;\n    for (int i = 2, pre = 0; i \u003C n;) {\n        if (s[i - 1] == s[pre]) next[i++] = ++pre;\n        else if (pre > 0) pre = next[pre];\n        else next[i++] = 0;\n    }\n    return next;\n}\n\nint main() {\n    ios::sync_with_stdio(0);\n    cin.tie(0);\n    string str, match;\n    getline(cin, str);\n    getline(cin, match);\n    int idx = kmp(str, match);\n    if (idx == -1) {\n        cout \u003C\u003C \"str 不存在子串 match\" \u003C\u003C '\\n';\n    } else {\n        printf(\"str 第一次出现 match 串的下标: %d\\n\", idx);\n    }\n    return 0;\n}\n",[64,2487,2485],{"__ignoreMap":428},[602,2489,2490],{"v-slot:tab2":428},[423,2491,2497],{"className":2492,"code":2494,"filename":2495,"language":2496,"meta":428},[2493],"language-java","import java.io.BufferedReader;\nimport java.io.InputStreamReader;\nimport java.io.OutputStreamWriter;\nimport java.io.PrintWriter;\nimport java.io.IOException;\n\npublic class code01kmp {\n    public static int[] DEFAULT_ARRAY = new int[]{-1, 0};\n\n    public static int[] getNextArr(String s, int n) {\n        if (n \u003C= 2) {\n            return DEFAULT_ARRAY;\n        }\n        int[] next = new int[n];\n        next[0] = -1;\n        next[1] = 0;\n        for (int i = 2, pre = 0; i \u003C n;) {\n            if (s.charAt(i - 1) == s.charAt(pre)) next[i++] = ++pre;\n            else if (pre > 0) pre = next[pre];\n            else next[i++] = 0;\n        }\n        return next;\n    }\n\n    public static int KMP(String str, String match) {\n        int n = str.length(), m = match.length();\n        if (n \u003C m) return -1;\n        int[] next = getNextArr(match, m);\n        int i, j;\n        for (i = 0, j = 0; i \u003C n && j \u003C m;) {\n            if (str.charAt(i) == match.charAt(j)) {\n                i++;\n                j++;\n            } else if (j == 0) {\n                ++i;\n            } else {\n                j = next[j];\n            }\n        }\n        return j == m ? i - j : -1;\n    }\n\n    public static void main(String[] args) throws IOException {\n        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));\n        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));\n        String str = in.readLine();\n        String match = in.readLine();\n        int idx = KMP(str, match);\n        if (idx == -1) {\n            out.println(\"str 不存在 match 子串\");\n        } else {\n            out.printf(\"str 第一次出现 match 子串下标: %d \\n\", idx);\n        }\n        out.flush();\n        out.close();\n        in.close();\n    }\n}\n","code01kmp.java","java",[64,2498,2494],{"__ignoreMap":428},[51,2500],{},[10,2502,2504],{"id":2503},"总结","总结 🎀",[29,2506,2508,2512],{"title":2507,"type":32,":card":44},"三句话记住 KMP",[602,2509,2510],{"v-slot:title":428},[19,2511,2507],{},[974,2513,2514,2521,2530],{},[80,2515,2516,2520],{},[37,2517,2518,149],{},[64,2519,129],{"code":129},"记录了模式串每个位置的最长公共前后缀长度。",[80,2522,2523,2526,2527,2529],{},[37,2524,2525],{},"匹配阶段","：失配时利用 ",[64,2528,129],{"code":129}," 跳转，已匹配的部分不再重比。",[80,2531,2532,2535,2536,2538,2539,2541],{},[37,2533,2534],{},"预处理阶段","：求 ",[64,2537,129],{"code":129}," 本身也是用 ",[64,2540,129],{"code":129}," 加速，思想自洽。",[60,2543,2544],{},[19,2545,2546],{},"最难的不是写出 KMP，而是理解为什么它是对的——理解了反证法那一步，KMP 就再也不会被忘记了。",[51,2548],{},[10,2550,2551],{"id":2551},"结语",[2553,2554,2555],"blur",{},"时隔一个月再次更新。",[19,2557,2558],{},"怎么说呢？感觉自己爆更刻意写博客会写得很烂很水，偶尔写一次反倒挺不错的。",[19,2560,2561,2562],{},"嗯，随缘更新吧——写一篇有质量的文章太难了，好难受。月更博主，加油读者朋友们。 ",[42,2563,2564],{":round":44},"共勉",[19,2566,2567],{},"希望这篇文章能帮助你理解 KMP 算法 💖 如有问题欢迎留言交流！",[423,2569,2575],{"className":2570,"code":2572,"language":2573,"meta":2574},[2571],"language-md","KMP算法的原理和Coding思路\n","md","wrap",[64,2576,2572],{"__ignoreMap":428},{"title":428,"searchDepth":2578,"depth":2578,"links":2579},4,[2580,2582,2583,2584,2589,2593,2600,2611,2616,2617,2618],{"id":12,"depth":2581,"text":12},2,{"id":55,"depth":2581,"text":55},{"id":166,"depth":2581,"text":167},{"id":372,"depth":2581,"text":373,"children":2585},[2586,2588],{"id":384,"depth":2587,"text":384},3,{"id":421,"depth":2587,"text":421},{"id":530,"depth":2581,"text":2590,"children":2591},"认识 next 数组 💡",[2592],{"id":564,"depth":2587,"text":564},{"id":670,"depth":2581,"text":2594,"children":2595},"如何用 next 数组加速匹配",[2596,2597,2598,2599],{"id":685,"depth":2587,"text":685},{"id":728,"depth":2587,"text":421},{"id":739,"depth":2587,"text":739},{"id":861,"depth":2587,"text":862},{"id":1002,"depth":2581,"text":2601,"children":2602},"如何快速求 next 数组",[2603,2609,2610],{"id":1024,"depth":2587,"text":2604,"children":2605},"已知 next[0..i-1]，求 next[i]",[2606,2607,2608],{"id":1042,"depth":2578,"text":1043},{"id":1084,"depth":2578,"text":1085},{"id":1184,"depth":2578,"text":1185},{"id":1210,"depth":2587,"text":421},{"id":1221,"depth":2587,"text":1221},{"id":155,"depth":2581,"text":155,"children":2612},[2613,2615],{"id":1344,"depth":2587,"text":2614},"next_array 的时间复杂度推导",{"id":2145,"depth":2587,"text":2145},{"id":2476,"depth":2581,"text":2476},{"id":2503,"depth":2581,"text":2504},{"id":2551,"depth":2581,"text":2551},[2620],"技术","2026-04-29 13:29:48",false,"\u002Fimages\u002Fposts\u002F0003_kmp.png",{"aside":2625,"slots":2628},[2626,2627],"toc","meta-aside-foo",{"aside-track":2629,"aside-foo":2642},{"props":2630,"type":7,"value":2632},{":card":44,"card":428,"title":2631},"point",[2633],[77,2634,2635,2636,2635,2639,2635],{},"\n",[80,2637,2638],{},"理解next数组的原理和求解",[80,2640,2641],{},"利用next数组加速匹配",{"props":2643,"type":7,"value":2645},{":card":44,"card":428,"title":2644},"🌸 学习路径建议",[2646,2649],[19,2647,2648],{},"学完 KMP 之后可以继续看：",[77,2650,2635,2651,2635,2657,2635,2663,2635],{},[80,2652,2653,2656],{},[37,2654,2655],{},"Z 函数","：另一种字符串匹配预处理",[80,2658,2659,2662],{},[37,2660,2661],{},"AC 自动机","：多模式串匹配（KMP + Trie）",[80,2664,2665,2668],{},[37,2666,2667],{},"后缀数组 \u002F 后缀自动机","：更强大的字符串数据结构",true,"\u002F2026\u002Fkmp-(knuth-morris-pratt)",null,{"text":2673,"minutes":2674,"time":2675,"words":2676},"15 min read",14.1,846000,2820,{"title":5,"description":5},{"loc":2670},"posts\u002F2026\u002FKMP-(Knuth-Morris-Pratt)",[45,2681],"Algorithm","tech","nObT_FU1JSlIAaYB9dRhYTMrmSWidfqY9Xiyj9IM_-8",[2685,2671],{"title":2686,"path":2687,"stem":2688,"date":2689,"type":2682,"children":-1},"high-concurrency-runtime:调度层设计","\u002F2026\u002Fhigh-concurrency-runtime","posts\u002F2026\u002Fhigh-concurrency-runtime\u002F调度层设计","2026-04-24 00:10:39",1777518298978]