數據結構 KMP 字符串匹配算法

KMP算法是計算機科學中的一種字符串匹配算法,KMP是三個創始人名字首字母

題目

AcWing - 算法基礎課

前置知識點

KMP算法是一種高效的字符串匹配算法,算法名稱取自于三位共同發明人名字的首字母組合。該算法的主要使用場景就是在字符串(也叫主串)中的模式串(也叫子串)定位問題,常見的有“求子串出現的起始位置”、“求子串的出現次數”等。

前綴和后綴

假設一個字符串“ababa"

他的前綴就是a,ab,aba,abab (ababa,ababa,ababa,ababa)

他的后綴就是a,ba,aba,baba (ababa,ababa,ababa,ababa

前綴和后綴的最大長度,是原字符串長度-1

思路

最淺顯易懂的 KMP 算法講解_嗶哩嗶哩_bilibili

F03【模板】KMP 算法——信息學競賽算法_嗶哩嗶哩_bilibili

給定一個主串S,模式串P,求P在S中出現的位置

普通暴力枚舉

一個字符一個字符的雙指針枚舉,一旦發現不同

主串又要從第二個開始匹配,子串要從頭開始匹配

缺點:時間復雜度太高

kmp優化思路

在比對失敗時,我們已經知道曾經讀過哪些字符了

已經匹配的子串,有相同的后綴, 前綴,那我們是不是可以保留相同的前綴,再去查找不同的剩下的字符串

如下圖,主串保留的綠色,實際上是匹配的子串的后綴,子串里保留的綠色部分,就是匹配的子串的前綴

他們是相同的,則可以把主串里匹配的子串的后綴,視為子串前綴,繼續在主串查找相同前綴后面的字符串

next數組使用

我們已經知道kmp的優化思路,那么如何將知道,該保留的前綴呢?再暴力雙指針循環太麻煩

kmp三個人提出了next數組,我們先不看如何實現,先看如何使用,next數組功能

下圖是一個初始狀態

kmp算法匹配失敗時,去看最后一個匹配成功的子串的字符,對應的next數組里的值

查到對應的值后,我們移動子串,跳過next數組里的值對應的字符個數,例如值是2,我們就跳過前兩個字符

我們發現,跳過的這兩個字符,確實能和主串指針指向位置前的兩個字符對應

所以繼續枚舉即可,不需要從頭枚舉

這樣,我們永遠不需要回退主串指針,一次遍歷主串,就可以找到匹配子串

再看一下 ,如果子串對應next是0時

如果為0,也是從頭開始匹配子串,沒有相同的前綴和后綴,則子串一定不在已經遍歷過的主串里有

則已經遍歷過的主串里的字符,一定沒有子串的子串,所以主串之前的部分可以直接舍棄,不用移動主串指針

推導next數組

next數組中的值,實際上就是,子串以當前字符串結尾,在當前字符串中,最長的前綴,后綴相同的字符串長度

如下圖

ABAB最長的相同前綴,后綴,該前綴或者后綴的長度是2

我們現在知道next的數組里的值代表什么了

那我們想一下,如何推導出next數組里的值

在后綴下一個字符,和前綴后一個字符相同時,我們只需要把next數組對應的值+1,然后填入即可

那在不同時,應該怎么辦,循環遍歷可以,但是時間復雜度較高

舉例,上圖再往下走一個

C和B不同,如何求B的對應的next值

下位字符不同,沒辦法構成更長的相同前后綴,我們看看有沒有更短的前后綴

我們可以發現,確實存在更短的,兩位的前后綴,但是這步在程序中如何體現,暴力求解似乎時間復雜度有點高

其實我們next數組里還有信息,也就是next[i]=3,則子串前3個數,和i-3到i,這兩個字符串是相同的

字符串前三個字符,和i-3到i,就是前后綴,前三個數,和后三個數

也就是右邊的后綴,其實等于左邊的前綴,那我們其實可以忽略中間其他的值,直接去找到最左邊

為什么不會有漏?第一,右邊的后綴和左邊的前綴完全相等

第二,后綴第四個字符和前綴第四個字符不同,所以不會比右邊的后綴,或左邊的前綴更長,也就不會用更多字符

這樣,就相當于是求ABAB的前后綴

第三個A對應的值是1,B是字符串ABAB后綴的第二個字符(因為A對應的是1)

比較B和前綴的第二個字符是否相等

相等,則在B所處位置對應的next數組的值+1

B對應的next的數組的上一位的值,是通過第三個A獲取的,但是實際上,和他組成后綴的是倒數第二個A

實際上是這樣得到的next對應的值,抽象上是和第三個A組成的前后綴

因為倒數第二個A,對應的next的值為3,也就是和前三個字符前綴完全相等

所以可以直接拿第二個A的next計算,因為前三個字符,和后三個字符(這說的字符串next=3對應的A)完全一樣

前三個有的字符,后三個都有,所以B可以和后三個字符組成的后綴,和前三個字符也可以組成相同的后綴

后三個字符代表的next值代表的前后綴相同的長度又太長,利用前三個字符代表的前后綴長度,即可完成回退

代碼實現

next[i]代表子串p[1,i]相同前后綴的最大長度

i代表的指針,永遠不往前回退,指向的是后綴的最后一個字符

j代表的指針,可以通過next數組回退,指向的是前綴最后一個字符

注:next在c++中有關鍵字,所以使用ne[]

next數組推導實現

ne[1]=0;
//兩個字符串數組實際上都是從1開始
//i等于2是因為指向第二個字符即可,至少兩個字符才有前后綴
//j=0,因為j從j+1開始比,也就是j=0是為了j從1開始
for(int i=2,j=0;i<=n;i++){                                                                                                   //j不為0,為0表示回到的子串第一個數的位置//i代表目前指到的位置,j代表相同前后綴的前綴的最后一個字符的位置//如果下位字符不同時,回退到j的值代表的前ne[j]位//上圖中說前綴后綴完全相等,j回到相同的前綴的最后一個字符的位置//看ne[j]的下一位p[j+],和i指向的字符是否相等//相等結束循環,否則繼續,直到相等或者j=0(回退到第一個字符串)while(j&&p[i]!=p[j+1])j=ne[j];//如果是相等,而不是j回退到了0,則j++,表示長度+1//j的值是ne[j]的值,j++=  就是  j=ne[j]+1,if(p[i]==p[j+1])j++;//記錄下j的值,j現在指向的是相同前后綴的前綴的最后一個字符//代表的值是最長相同前后綴長度//把這個值加在ne[i]指針上,指向的是相同前后綴后綴的最后一個字符ne[i]=j;
} 

子串位置查找實現,查找主串出現子串的起始位置

 //推導子串出現位置,這次i等于1是因為,此循環判斷的不是前后綴相同,而是判斷是否為子串//因為兩個完全一樣的字符串,也互相為子串,子串第一個數有可能從i=1,j=1就開始了//所以這里要令i=1for(int i=1,j=0;i<=m;i++){//跳躍式找到主串現在指向的值,在子串中存在的位置while(j&&s[i]!=p[j+1])j=ne[j];//i指向主串的字符,和子串的字符下一個字符相等,則可以再推進走一步if(s[i]==p[j+1])j++;if(j==n)//長度一致,找到子串{cout<<i-j<<" ";//返回子串起始位置//可省略,意思是將j回退到除本身外,最大的相同前后綴長度,避免下個循環j+1越界j=ne[j];}}

整合代碼

AcWing - 算法基礎課

#include<iostream>
using namespace std;
const int N = 100010;
char p[N],s[N*10];
int ne[N];
int n,m;
int main(){cin >> n >> p + 1 >> m >> s + 1;ne[1]=0;//推導nxet數組for(int i=2,j=0;i<=n;i++){while(j&&p[i]!=p[j+1])j=ne[j];if(p[i]==p[j+1])j++;ne[i]=j;}//推導子串出現位置for(int i=1,j=0;i<=m;i++){//跳躍式找到主串現在指向的值,在子串中存在的位置while(j&&s[i]!=p[j+1])j=ne[j];if(s[i]==p[j+1])j++;if(j==n)//長度一致,找到子串{cout<<i-j<<" ";//返回子串起始位置//可省略,意思是將j回退到除本身外,最大的相同前后綴長度,避免下個循環j+1越界j=ne[j];}}return 0;
}

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

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

相關文章

Conda配置Python環境

1. 安裝 Conda 選擇發行版&#xff1a; Anaconda&#xff1a;適合需要預裝大量科學計算包的用戶&#xff08;體積較大&#xff09;。 Miniconda&#xff1a;輕量版&#xff0c;僅包含 Conda 和 Python&#xff08;推薦自行安裝所需包&#xff09;。 驗證安裝&#xff1a; co…

數倉開發那些事(11)

某神州優秀員工&#xff1a;一閃&#xff0c;領導說要給我漲米。 一閃&#xff1a;。。。。&#xff08;著急的團團轉&#xff09; 老運維&#xff1a;Oi&#xff0c;兩個吊毛&#xff0c;看看你們的hadoop集群&#xff0c;健康度30分&#xff0c;怎么還在抽思謀克&#xff1f…

MyBatis Plus 中 update_time 字段自動填充失效的原因分析及解決方案

? MyBatis Plus 中 update_time 字段自動填充失效的原因分析及解決方案 前言一、問題現象二、原因分析1. 使用了 strictInsertFill/strictUpdateFill 導致更新失效2. 實體類注解配置錯誤3. MetaObjectHandler 未生效4. 使用自定義 SQL 導致自動填充失效5. 字段類型不匹配 三、…

C++ STL常用算法之常用算術生成算法

常用算術生成算法 學習目標: 掌握常用的算術生成算法 注意: 算術生成算法屬于小型算法&#xff0c;使用時包含的頭文件為 #include <numeric> 算法簡介: accumulate // 計算容器元素累計總和 fill // 向容器中添加元素 accumulate 功能描述: 計算區間內容器元素…

axios基礎入門教程

一、axios 簡介 axios 是一個基于 Promise 的 HTTP 客戶端&#xff0c;可用于瀏覽器和 Node.js 環境&#xff0c;支持以下特性&#xff1a; 發送 HTTP 請求&#xff08;GET/POST/PUT/DELETE 等&#xff09; 攔截請求和響應 自動轉換 JSON 數據 取消請求 并發請求處理 二…

短視頻團隊架構工作流程---2025.3.30 李劭卓

短視頻團隊架構&工作流程—2025.3.30 李劭卓 文章目錄 短視頻團隊架構&工作流程---2025.3.30 李劭卓1 工作職責1.1 編劇&#xff1a;1.2 主編&#xff1a;1.3 總編&#xff1a;1.4 導演&#xff1a;1.5 攝影&#xff1a;1.6 演員&#xff1a;1.7 后期&#xff1a;1.8 美…

MySQL 高效 SQL 使用技巧詳解

MySQL 高效 SQL 使用 技巧詳解 一、為什么需要優化 SQL&#xff1f; 性能瓶頸&#xff1a;慢查詢導致數據庫負載升高&#xff0c;響應時間延長。資源浪費&#xff1a;低效 SQL 可能占用大量 CPU、內存和磁盤 I/O。 目標&#xff1a;通過優化 SQL 將查詢性能提升 10 倍以上&am…

AI基礎03-視頻數據采集

上篇文章我們學習了圖片的數據采集&#xff0c;今天主要了解一下視頻數據采集的方法。視頻是由一系列圖像構成的&#xff0c;其中每一張圖片就是一幀。視頻數據采集方法通常有自動圖像采集和基于處理器的圖像采集兩種。我們學習一下如何利用python 工具和筆記本計算機攝像頭進行…

Scala 數組

Scala 數組 引言 Scala 作為一門多范式編程語言&#xff0c;融合了面向對象和函數式編程的特點。數組是編程語言中非常基礎和常見的數據結構&#xff0c;在 Scala 中也不例外。本文將詳細介紹 Scala 中的數組&#xff0c;包括其定義、操作以及在實際開發中的應用。 Scala 數…

Text-to-SQL將自然語言轉換為數據庫查詢語句

有關Text-To-SQL方法&#xff0c;可以查閱我的另一篇文章&#xff0c;Text-to-SQL方法研究 直接與數據庫對話-text2sql Text2sql就是把文本轉換為sql語言&#xff0c;這段時間公司有這方面的需求&#xff0c;調研了一下市面上text2sql的方法&#xff0c;比如阿里的Chat2DB,麻…

golang 的strconv包常用方法

目錄 1. 字符串與整數的轉換 2. 字符串與浮點數的轉換 3. 布爾值的轉換 4. 字符串的轉義 5. 補充&#xff1a;rune 類型的使用 方法功能詳解 代碼示例&#xff1a; 1. 字符串與整數的轉換 方法名稱功能描述示例Atoi將字符串轉換為十進制整數。strconv.Atoi("123&q…

MATLAB詳細圖文安裝教程(附安裝包)

前言 MATLAB&#xff08;Matrix Laboratory&#xff09;是由MathWorks公司開發的一款高性能的編程語言和交互式環境&#xff0c;主要用于數值計算、數據分析和算法開發。內置數學函數和工具箱豐富&#xff0c;開發效率高&#xff0c;特別適合矩陣運算和領域特定問題。接下來就…

ShapeCrawler:.NET開發者的PPTX操控魔法

引言 在當今的軟件開發領域&#xff0c;隨著數據可視化和信息展示需求的不斷增長&#xff0c;處理 PPTX 文件的場景日益頻繁。無論是自動化生成報告、批量制作演示文稿&#xff0c;還是對現有 PPT 進行內容更新與格式調整&#xff0c;開發者都需要高效的工具來完成這些任務。傳…

HTML5貪吃蛇游戲開發經驗分享

HTML5貪吃蛇游戲開發經驗分享 這里寫目錄標題 HTML5貪吃蛇游戲開發經驗分享項目介紹技術棧核心功能實現1. 游戲初始化2. 蛇的移動控制3. 碰撞檢測4. 食物生成 開發心得項目收獲后續優化方向結語 項目介紹 在這個項目中&#xff0c;我使用HTML5 Canvas和原生JavaScript實現了一…

有關pip與conda的介紹

Conda vs. Pip vs. Virtualenv 命令對比 任務Conda 命令Pip 命令Virtualenv 命令安裝包conda install $PACKAGE_NAMEpip install $PACKAGE_NAMEX更新包conda update --name $ENVIRONMENT_NAME $PACKAGE_NAMEpip install --upgrade $PACKAGE_NAMEX更新包管理器conda update con…

【Linux】調試器——gdb使用

目錄 一、預備知識 二、常用指令 三、調試技巧 &#xff08;一&#xff09;監視變量的變化指令 watch &#xff08;二&#xff09;更改指定變量的值 set var 正文 一、預備知識 程序的發布形式有兩種&#xff0c;debug和release模式&#xff0c;Linux gcc/g出來的二進制…

【Ubuntu常用命令】

1.將本地服務器文件或文件夾傳輸到遠程服務器 文件 scp /data/a.txt administrator10.60.51.20:/home/administrator/ 文件夾 scp -r /data/ administrator10.60.51.20:/home/administrator/ 2.從遠程服務器傳輸文件到本地服務器 scp administrator10.60.51.20:/data/a.txt /h…

golang 的time包的常用方法

目錄 time 包方法總結 類型 time.Time 的方法 庫函數 代碼示例&#xff1a; time 包方法總結 類型 time.Time 的方法 方法名描述示例               ?Now()獲取當前時間和日期time.Now()Format()格式化時間為字符串time.Now().Format("2006-01-02 15…

Elasticsearch:使用 Azure AI 文檔智能解析 PDF 文本和表格數據

作者&#xff1a;來自 Elastic James Williams 了解如何使用 Azure AI 文檔智能解析包含文本和表格數據的 PDF 文檔。 Azure AI 文檔智能是一個強大的工具&#xff0c;用于從 PDF 中提取結構化數據。它可以有效地提取文本和表格數據。提取的數據可以索引到 Elastic Cloud Serve…

【ArcGIS操作】ArcGIS 進行空間聚類分析

ArcGIS 是一個強大的地理信息系統&#xff08;GIS&#xff09;軟件&#xff0c;主要用于地理數據的存儲、分析、可視化和制圖 啟動 ArcMap 在 Windows 中&#xff0c;點擊“開始”菜單&#xff0c;找到 ArcGIS文件夾&#xff0c;然后點擊 ArcMap 添加數據 添加數據 - 點擊工具…