力扣刷題總結 字符串(2)【KMP】

🔥博客主頁:?A_SHOWY
🎥系列專欄:力扣刷題總結錄?數據結構??云計算??數字圖像處理? ??

28.找出字符串中第一個匹配項的下標mid經典KMP
4593重復的子字符串mid可以使用滑動窗口或者KMP

KMP章節難度較大,需要深入理解其中的底層原理,單純背代碼不可靠

一、KMP方法總結

(1)KMP能解決的問題

KMP主要應用在字符串匹配上

(2)前綴和后綴

前綴:包含首字母不包含尾字母的所有子串

例如:

字符串aabaaf
前綴: aaaaabaabaaabaaaabaaf ×

后綴:包含尾字母而不包含首字母的所有子串

字符串aabaaf
后綴: fafaafbaafabaafaabaaf ×

(3)最長相等的前后綴?

模式串的前綴表

字符串aabaaf
最長相等前后綴:
a      0
aa     1
aab    0
aaba   1
aabaa  2
aabaaf 0
所以前綴表是010120

所以要跳到最長的相等前后綴的后面去接著匹配

(4)前綴表原理

模式串與前綴表對應位置的數字表示的是:下標i之前的字符串中,有多大長度的相同前后綴,所以當找到不匹配的位置時,要看前一個字符的前綴表的數值,因為要找前一個字符的最長相同的前后綴,前一個字符的前綴表的數值是2, 所以把下標移動到下標2的位置繼續比配。next(prefix)數組:遇到沖突后,要回退到的下一個位置

最重要的一點(理解KMP的核心)為什么使用前綴表可以告訴我們匹配失敗之后跳到哪里重新匹配?

假設在下標為5的部分不匹配了,下標5之前的這部分字符串最長相等的前后綴是aa,找到了最長的相等前后綴,匹配失敗的地方是后綴子串的后面,那么找到與其相等的前綴部分就可以繼續匹配了。

(5)前綴表代碼實現

1.求next數組方式有很多 (比如原封不動【如果遇到沖突了,就找這個前綴表數組的前一位作為下標】,有的全部右移【初始值為-1,沖突這個位置對應下標】,有的全部減去1【不推薦 】),但是next數組的核心是遇到沖突了要向前回退

2.i指向了后綴末尾,j指向了前綴末尾,還代表i(包括i)之前的子串的最長相等前后綴的長度(這里難點就是j的雙層含義),他非常像一個遞歸的過程

3.具體步驟可以分為四步:初始化 討論處理前后綴不相同的時候 處理前后綴相同的時候 給next數組賦值,就能得到模式串的前綴表

  • 首先是初始化,對于 i和j的初始化以及next【0】的初始化,那么j我們初始化為-1,前面提到這只是其中的一種表現形式也就是串整體右移,那么i選擇1(i肯定不能是0),next【i】表示i之前最長前后綴的長度,其實也就是j對于i的初始化我們放在后邊的循環里
int j = -1;
next[0] = j;
  • 其次就是討論前后綴不相同的情況,比較s【i】和s【j+1】是否相同,如果不同,那么就要回退,由于next【j】記錄著j之前子串相同前后綴的長度,就要找j+1前一個元素在next數組的值(next【j】),注意向前回退是一個持續的過程,所以用while如果不匹配就一直回退
for(int i = 0; i < s.size(); s++){
while(j>=0 && s[i] != s[j+1]){//向前回退j = next[j];
}
}
  • 然后處理前后綴相同的情況,如果s【i】和s【j+1】相同,,就要同時移動i和j,并且,講j賦值給next【i】
if (s[i] == s[j + 1]) { // 找到相同的前后綴j++;
}
next[i] = j;

?所以完整版本的 代碼如下:

void getNext(int* next, const string& s){int j = -1;next[0] = j;for(int i = 1; i < s.size(); i++) { // 注意i從1開始while (j >= 0 && s[i] != s[j + 1]) { // 前后綴不相同了j = next[j]; // 向前回退}if (s[i] == s[j + 1]) { // 找到相同的前后綴j++;}next[i] = j; // 將j(前綴的長度)賦給next[i]}
}

二、經典題目

(1)28.找出字符串中第一個匹配項的下標

28. 找出字符串中第一個匹配項的下標icon-default.png?t=N7T8https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/其實這個題目可以分成兩個部分,是一個經典的字符串匹配題目,肯定是用我們剛剛學過的KMP算法來完成。那么所謂分成兩個部分解體,第一部分就是構造NEXT數組,第二部分就是運用這個前綴表進行一個匹配的問題,那么第一部分的構造前綴表(NEXT數組)已經寫完了,下面我們來寫匹配過程的代碼。注意我們一直用的是全體-1操作后的NEXT數組,在文本串haystack里面查找是否出現過模式串needle。

 int strStr(string haystack, string needle) {int j = -1;int next[needle.size()];getNext(next,needle);for(int i = 0; i< haystack.size();i++)//匹配過程中,next數組的使用從索引0開始{while(j >= 0 && haystack[i] != needle[j+1]){j = next[j];}if(haystack[i] == needle[j+1]){j++;}if(j == needle.size() -1){//j到了末尾return (i-needle.size() +1);//返回首部}}return -1;}

這里有幾個要注意的點,第一個是i的初始值,匹配過程中,next數組的使用從索引0開始,在構造next數組的時候,0已經給了初始值了,所以從1開始。

然后就是如何判斷是否包含呢,當i走到了needle的末尾的時候,說明存在匹配,返回首部的數值即可,所以完整的代碼實現為

class Solution {
public:
void getNext(int* next, string& s){int j = -1;next[0] = j;//初始化for(int i =1; i < s.size();i++)//初始化,i從1開始{while(j >= 0 && s[i] != s[j+1]){//如果不想等j = next[j];//j就回退,這里有點像遞歸}if(s[i] == s[j+1]){j++;}next[i] = j;//最后給next數組的i坐標賦值}
}int strStr(string haystack, string needle) {int j = -1;int next[needle.size()];getNext(next,needle);for(int i = 0; i< haystack.size();i++)//匹配過程中,next數組的使用從索引0開始{while(j >= 0 && haystack[i] != needle[j+1]){j = next[j];}if(haystack[i] == needle[j+1]){j++;}if(j == needle.size() -1){//j到了末尾return (i-needle.size() +1);//返回首部}}return -1;}
};

(2)459.重復的子字符串

459. 重復的子字符串icon-default.png?t=N7T8https://leetcode.cn/problems/repeated-substring-pattern/

方法一:滑動窗口

當一個字符串內部由重復的子串組成,也就是由前后相同的子串組成。那么既然前面有相同的子串,后面有相同的子串,用 s + s,這樣組成的字符串中,后面的子串做前串,前面的子串做后串,就一定還能組成一個s。

所以總體的思路為(個人認為很神奇的思路):只要兩個s拼接在一起,里面還出現一個s的話,就說明是由重復子串組成。但是注意要去除s+s的字符串的首尾位置,不然判斷沒有意義。

class Solution {
public:bool repeatedSubstringPattern(string s) {string t = s + s;//掐頭去尾t.erase(t.begin());t.erase(t.end() -1);//注意是end-1,應為t.end,返回指向t末尾下一個位置的迭代器if(t.find(s) != std::string::npos)//指的是字符串中未找到指定內容時的特殊返回值return true;return false;}
};

方法二:KMP算法

為什么可以使用KMP

步驟一:因為 這是相等的前綴和后綴,t[0] 與 k[0]相同, t[1] 與 k[1]相同,所以 s[0] 一定和 s[2]相同,s[1] 一定和 s[3]相同,即:,s[0]s[1]與s[2]s[3]相同 。

步驟二: 因為在同一個字符串位置,所以 t[2] 與 k[0]相同,t[3] 與 k[1]相同。

步驟三: 因為 這是相等的前綴和后綴,t[2] 與 k[2]相同 ,t[3]與k[3] 相同,所以,s[2]一定和s[4]相同,s[3]一定和s[5]相同,即:s[2]s[3] 與 s[4]s[5]相同。

步驟四:循環往復。

結論:在由重復子串組成的字符串中,最長前后綴不包含的子串就是最小重復子串

最長相等前后綴的長度為:next[len - 1] + 1。(這里的next數組是以統一減一的方式計算的,因此需要+1,如果len % (len - (next[len - 1] + 1)) == 0 ,則說明數組的長度正好可以被 (數組長度-最長相等前后綴的長度) 整除 ,說明該字符串有重復的子字符串。?(仍然是很神奇的思路)

class Solution {
public:void getNext (int* next, const string& s){next[0] = -1;int j = -1;for(int i = 1;i < s.size(); i++){while(j >= 0 && s[i] != s[j + 1]) {j = next[j];}if(s[i] == s[j + 1]) {j++;}next[i] = j;}}bool repeatedSubstringPattern (string s) {if (s.size() == 0) {return false;}int next[s.size()];getNext(next, s);int len = s.size();if (next[len - 1] != -1 && len % (len - (next[len - 1] + 1)) == 0) {return true;}return false;}
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/211544.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/211544.shtml
英文地址,請注明出處:http://en.pswp.cn/news/211544.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Flink 本地單機/Standalone集群/YARN模式集群搭建

準備工作 本文簡述Flink在Linux中安裝步驟&#xff0c;和示例程序的運行。需要安裝JDK1.8及以上版本。 下載地址&#xff1a;下載Flink的二進制包 點進去后&#xff0c;選擇如下鏈接&#xff1a; 解壓flink-1.10.1-bin-scala_2.12.tgz&#xff0c;我這里解壓到soft目錄 [ro…

OrangePi ZERO2 刷機與啟動

鏡像準備 用讀卡器和Win32Diskimager刷寫鏡像到內存卡&#xff0c;鏡像文件見下面百度云鏈接&#xff1a;https://pan.baidu.com/s/14aKTznc4Jvw4SoFF54JUTg 提取碼&#xff1a;1815 刷寫完畢后插回香橙派 串口登錄 用MobaXterm和USB-TTL進行串口登錄&#xff0c;MobaXterm軟…

談一談網絡協議中的應用層

文章目錄 一&#xff0c;什么是HTTPHTTP的優缺點HTTPS 一&#xff0c;什么是HTTP 我們在通過網絡進行傳輸數據時&#xff0c;我們要保證&#xff0c;我們在發送時構造的數據&#xff0c;在接收時也能夠解析出來&#xff0c;這本質上就是一種協議&#xff0c;是一種應用層協議&…

Spring Cloud + Vue前后端分離-第3章 SpringBoot項目技術整合

Spring Cloud Vue前后端分離-第3章 SpringBoot項目技術整合 3-1 集成持久層框架Mybatis ORM:對象關系映射&#xff0c;Hibernate是全自動ORM&#xff0c;Mybatis是半自動ORM&#xff0c;Mybatis可以操作的花樣更多&#xff0c;是首選的持久層框架 System模塊集成Mybatis框架…

整數分析 C語言xdoj43

問題描述 給出一個整數n&#xff08;0<n<100000000&#xff09;。求出該整數的位數&#xff0c;以及組成該整數的所有數字中的最大數字和最小數字。 輸入說明 輸入一個整數n&#xff08;0<n<100000000&#xff09; 輸出說明 在一行上依次輸出整數n的位…

Linux內核上游提交完整流程及示例

參考博客文章&#xff1a; 向linux內核提交代碼 - 知乎 一、下載Linux內核源碼 通過git下載Linux內核源碼&#xff0c;具體命令如下&#xff1a; git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 實際命令及結果如下&#xff1a; penghaoDin…

IBM Qiskit量子機器學習速成(六)

量子卷積神經網絡 卷積和池化&#xff1a;卷積神經網絡的必備成分 卷積神經網絡被廣泛應用于圖像和音頻的識別當中&#xff0c;關鍵在于“卷積”操作賦予神經網絡統籌學習數據的能力。 執行卷積操作需要輸入數據與卷積核&#xff0c;卷積核首先與輸入數據左上角對齊&#xf…

【數據庫】簡單連接嵌套查詢

目錄 &#x1f387;簡單查詢 &#x1f387;連接查詢 &#x1f387;嵌套查詢 分析&思考 &#x1f387;簡單查詢 --練習簡單查詢 --select * from classes --select * from student --select * from scores --1.按Schedule表的結構要求用SQL語言創建Schedule表 --字段名…

深度學習之全面了解預訓練模型

在本專欄中&#xff0c;我們將討論預訓練模型。有很多模型可供選擇&#xff0c;因此也有很多考慮事項。 這次的專欄與以往稍有不同。我要回答的問題全部源于 MathWorks 社區論壇&#xff08;ww2.mathworks.cn/matlabcentral/&#xff09;的問題。我會首先總結 MATLAB Answers …

關于Linux Kernel Panic導致重啟的簡單分析步驟

Linux系統Kernel Panic的檢索 如何判斷是否發生Kernel Panic&#xff0c;以下以 CentOS 7.9系統為例 #查看 /var/crash 路徑下是否有生成文件夾&#xff0c;Kernel Panic后會生成文件夾在此路徑表示產生了Kernel Panic ls /var/crash #/var/crash/127.0.0.1-2023-12-04-08\:5…

HarmonyOS應用開發者基礎認證考試(穩過)

判斷題 ??????? 1. Web組件對于所有的網頁都可以使用zoom(factor: number)方法進行縮放。錯誤(False) 2. 每一個自定義組件都有自己的生命周期正確(True) 3. 每調用一次router.pushUrl()方法&#xff0c;默認情況下&#xff0c;頁面棧數量會加1&#xff0c;頁面棧支持的…

linux redis-cluster ipv6方式

配置文件&#xff0c;具體字段的含義&#xff0c;可以參考其他文檔。 1.單個文件的配置信息 redis_36380.conf requirepass Paas_2024port 36380tcp-backlog 511timeout 0tcp-keepalive 300daemonize yessupervised nopidfile /data/paas/apps/aicache-redis/redis_36380.p…

【STM32】TIM定時器編碼器

1 編碼器接口簡介 Encoder Interface 編碼器接口 編碼器接口可接收增量&#xff08;正交&#xff09;編碼器的信號&#xff0c;根據編碼器旋轉產生的正交信號脈沖&#xff0c;自動控制CNT自增或自減&#xff0c;從而指示編碼器的位置、旋轉方向和旋轉速度 接收正交信號&#…

黑豹程序員-EasyExcel實現導出

需求 將業務數據導出到excel中&#xff0c;老牌的可以選擇POI&#xff0c;也有個新的選擇EasyExcel。 有個小坑&#xff0c;客戶要求樣式比較美觀&#xff0c;數字列要求千位符&#xff0c;保留2位小數。 可以用代碼實現但非常繁瑣&#xff0c;用模板就特別方便&#xff0c;模…

C++優秀串口庫

serial::Serial Class Reference #include <serial.h> Data Structures class ScopedReadLockclass ScopedWriteLock Public Member Functions公有成員方法&#xff08;編程用的都在這里了&#xff0c;那些私有的如果不開源一般跟我們沒有關系了&#xff09; Serial …

用chatGPT開發項目:我想的無人的智慧樹網站 流量之神 利用人工智能的算法將人吸引住 GPT4是不是越來越難用了,問一下就要證明一下自己是不是人類

廣度發散&#xff1a;讓AI給出時代或今日或你關注的熱點事件 比如采集新聞頭條&#xff0c;根據內容或標題&#xff0c;以不同的角度&#xff0c;或各種人群的角色&#xff0c;生成50篇簡短的文章。一下就能占傳統的搜索引擎。這是AI最擅長的【千人千面&#xff0c;海量生成】…

【中國海洋大學】操作系統隨堂測試6整理

1. IO系統的層次機構包括&#xff1a;IO硬件、中斷處理程序、&#xff08;&#xff09;程序、設備獨立性軟件、用戶層軟件。 答&#xff1a;設備驅動 2. IO設備和控制器之間的接口包括三種類型的信號&#xff1a;數據信號線、控制信號線和&#xff08;&#xff09;&#xff1…

qt反射基礎

最近研究了一下QT的反射機制&#xff0c; Qt的元對象系統除了提供信號/槽機制的特性之外&#xff0c;它還提供了以下特性: QObject::metaObject() 返回關聯的元對象 QMetaObject::className() 在運行時狀態下返回類名 QObject::inherits() 判斷類的繼承關系 QObject::tr()&…

鴻蒙開發之封裝優化

面向對象開發離不開封裝&#xff0c;將重復的可以復用的代碼封裝起來&#xff0c;提高開發效率。 基于之前的List&#xff0c;對代碼進行封裝。 1、抽取component 將List的頭部抽離出來作為一個新的component。可以創建一個新的ArkTS文件&#xff0c;寫我們的頭部代碼 為了…

代理模式:解析對象間的間接訪問與控制

目錄 引言 理解代理模式 不同類型的代理模式 代理模式的應用場景 代理模式的優缺點 優點 缺點 實際案例&#xff1a;Java中的代理模式應用 結語 引言 代理模式是軟件設計模式中的一種結構型模式&#xff0c;旨在為其他對象提供一種代理以控制對這個對象的訪問。它允許你…