主頁 >  其他 > KMP演算法及其改進演算法

KMP演算法及其改進演算法

2020-09-11 10:14:43 其他

字符儲存在1~length的位置上

簡單模式匹配

? 思路:從主串的第一個位置起和模式串的第一個字符開始比較,如果相等,則繼續逐一比較后續字符;否則從主串的第二個字符開始,再重新用上一步的方法與模式串中的字符做比較,以此類推,直到比較完模式串中的所有字符,若匹配成功,則回傳模式串在主串中的位置;若匹配不成功,則回傳一個可區別于主串所有位置的標記,如“0”,

int index(Str str,Str substr)
{
    int i = 1,j = 1,k = 1;//串從陣列下標1位置開始存盤,初值為1
    while(i <= str.length && j <= substr.length)
    {
        if(str.ch[i] == substr[j])
        {
            i++;
            j++;
        }
        else
        {
            j = 1;
            i = ++k;//匹配失敗,i從主串的下一個位置開始匹配,k儲存了主串上一次的起始位置
        }
    }
    if(j > substr.length)
        return k;
    else ruturn 0;
}

主串(ABABCABCACBAB)和模式串(ABCAC)匹配程序

KMP演算法

? 設主串為s1s2...sn,模式串為p1p2...pm,在上面的匹配程序中,經常出現一個關鍵狀態(表2),其中i和j分別為主串和模式串中當前參與比較的兩個字符的下標,

? 模式串的前部某子串P1P2...Pj-1與主串中的一個子串Si-j+1Si-j+2...Si-1匹配,而Pj與Si不匹配,每當出現這種狀態時,簡單模式匹配演算法的做法是:一律將i賦值為i-j+2,j賦值為1,重新開始比較,這個程序反映到表2中可以形象地表示為模式串先向后移動一個位置,然后從第一個字符P1開始逐個和當前主串中對應的字符做比較;當再次發現不匹配時,重復上述程序,這樣做的目的是試圖消除Si處的不匹配,進而開始Si+1及其以后字符的比較,使得整個程序得以推進下去,

? 如果在模式串后移的程序中又出現了其前部某子串P1P2…與主串中某子串…Si-2Si-1相匹配的狀態,則認為這是一個進步的狀態,因為通過模式串后移排除了一些不可能匹配的狀態,來到了一個新的區域匹配狀態,并且此時Si有了和模式串中對應字符匹配的可能性,為了方便表述,記表中描述的狀態為Sk,此處的新狀態為Sk+1,此時可以將簡單模式匹配程序看成一個由Sk向Sk+1推進的程序,當由Sk來到Sk+1時有兩種情況可能發生:其一,S處的不匹配被解決,從si+1繼續往下比較,若來到新的不匹配字符位置,則模式串后移尋找狀態Sk+2;其二,Si處的不匹配仍然存在,則模式串繼續后移尋找狀態Sk+2如此進行下去,直到得到最終結果,

? 說明:為了使上邊其一與其二的表述看起來清晰工整且抓住重點,此處省略了對匹配成功與失敗這兩種容易理解的情況的描述,

? 說明:模式串后移使P1移動到Si+1,即模式串整個移過Si的情況也認為是Si處的不匹配被解決,試想,如果在匹配程序中可以省略掉模式串逐漸后移的程序,而從Sk直接跳到Sk+1,則可以大大提高匹配效率,帶著這個想法,我們把Sk+1狀態添加到表2中得到表3,

? 觀察發現,P1P2...Pj-1和Si-j+1Si-j+2...Si-1是完全相同的,且我們研究的是從Sk跳到Sk+1,因此,洗掉表3關于主串的一行,得到表4,

? 由表4可知,P1P2...Pt-1和Pj-t+1Pj-t+2...Pj-1匹配,記P1P2..Pj-1為F,記P1P2...Pt-1為FL,記Pj-t+1Pj-t+2...Pj-1為FR,所以,只需將F后移到使得FL和FR重合的位置(上表有色部位),即可實作Sk直接跳到Sk+1,

? 總結一般情況:每當發生不匹配的時候,找出模式串當中的不匹配的那個字符Pj,取其之前的子串F=P1P2...Pj-1,將模式串后移,使F最先發生前部(FL)與后部(FR)相重合的位置(見表中有色區域所示),即為模式串應后移的目標位置,

? 為了使問題表述得更形象,采用了模式串后移這種分析方式,事實上,在計算機中模式串是不會移動的,因此需要把模式串后移轉化為j的變化,模式串后移到某個位置可等效于j重新指向某位置,容易看出,j處發生不匹配時,j重新指向的位置恰好是F串中前后相重合子串的長度+1(串F或F長度+1),通常我們定義一個next]陣列,其中j取1~m,m為模式串長度,表示模式串中第j個字符發生不匹配時,應從next]處的字符開始重新與主串比較,
特殊情況:
? 1)模式串中的第一個字符與主串i位置不匹配,應從下一個位置和模式串第一個字符繼續比較,反映在從si+1與p1開始比較,
? 2)當串F中不存在前后重合的部分時(不可將F自身視為和自身重合),則從主串中發生不匹配的字符與模式串第一個字符開始比較,反映在表4-2中即從s1與p1開始比較,
下邊以下表中的模式串為例,介紹求陣列next的方法,

? 1)當j等于1時發生不匹配,屬于特殊情況1,此時將next[1]賦值成0來表示這個特殊情況,
? 2)當j等于2時發生不匹配,此時F為“”,屬于特殊情況2),即next[2]賦值為1,
? 3)當j等于3時發生不匹配,此時F為“AB”,屬于特殊情況2),即next[3]賦值為1,
? 4)當j等于4時發生不匹配,此時F為“ABA”,前部子串A與后部子串A重合,長度為1,因此next[4]賦值為2(F或FR長度+1),
? 5)當j等于5時發生不匹配,此時F為“ABAB”,前部子串AB與后部子串AB重合,長度為2,因此next[5]賦值為3,
? 6)當j等于6時發生不匹配,此時F為“ABAB”,前部子串ABA與后部子串ABA最先發生重合,長度為3,因此next[6]賦值為4,
? 7)當j等于7時發生不匹配,此時F為“ABABAB”,前部子串ABAB與后部子串ABAB最先發生
重合,長度為4,因此next[7]賦值為5,

? 注意:6)和7)中出現了“最先"字眼,以7)為例,F向后移動,會發生兩次前部與后部的重合,第一次是ABAB,第二次是AB,顯然最先發生重合的是ABAB.之所以選擇最先的ABAB,而不是第二次的AB,是因為模式串是不停后移的,選擇AB則丟掉了一次解決不匹配的可能性,而選擇ABAB,即使當前解決不了,則下一個狀態就是AB,不會丟掉任何解決問題的可能,這里也解釋了一些參考書中提到的取最長相等前后的原因,7)中的ABAB或AB在一些參考書中稱為F的相等前后綴(即FL和FR為F的相等前后綴),ABAB是最長相等前后綴,并且很顯然的是,越先發生重合的相等前后綴長度越長,

next陣列

? 上述方法為手工求next陣列的方法,介紹一下適用于轉換成代碼的高效的求next陣列的方法,

? 假設next[j]的值已知,則next[j+1]的求值可以分兩種情況分析,

? 1)若Pj等于Pt,顯然next[j+1]=t+1,因為t為當前F最長相等前后綴長度(t為FL和FR長度),
? 2)若Pj不等于Pt,將Pj-t+1Pj-t+2...Pj當作主串,P1P2...Pt當作子串,則又回到了由狀態Sk找Sk+1的程序,所以只需將t賦值為next[t],繼續進行Pj與Pt的比較,如果滿足1)則求得next[j+1],不滿足則重復t賦值為next[t],并比較Pj與Pt的程序,如果在這個程序中t出現等于0的情況,則應將next[J+1]賦值為1,此處類似于上邊講到的特殊情況2),
? 說明:Sk直接跳到Sk+1,也就是通常所說的簡單模式匹配演算法中i不需要回溯,
注意:MP演算法中的i不需要回溯這里隱藏著一個考點,i不需要回溯意味著對于規模較大的外存中字串的匹配操作可以分段進行,讀入記憶體一部分進行匹配,完成之后即可寫回外存確保在發生不匹配時不需要將之前寫回外存的部分再次讀入,減少了IO操作,提高了效率,在回答KMP演算法較之于簡單模式匹配演算法的優勢時,不要忘掉這一點,

演算法如下

void getnext(Str substr,int next[])
{
	int i = 1,j = 0;//串從下標為1的位置開始存盤,i初值為1
	next[1] = 0;
	while(i < substr.length)
    {
		if(j == 0 || substr.ch[i] == sbustr[j])
        {
			++i;
			++j;
			next[i] = j;
		}
		else
			j = next[j]//理解這一點,回溯
	}
}

? 得到next陣列后,將簡單模式匹配演算法稍作修改就可以由狀態Sk直接跳到Sk+1的改進演算法,這就是知名的KMP演算法,代碼如下:

int KMP(Str str,Str substr,int next[])
{
	int i = 1,j = 1;//串從陣列下標1處開始
	while(i <= str.length && j <= substr.length)
    {
		if(j == 0 || str.ch[i] == substr.ch[j])
        {
			++i;
			++j;
		}
		else
			j = next[j];
	}
	if(j > substr.length)
		return i - substr.length;
	else
		return 0;
}

KMP演算法的改進

? 先看一種特殊情況,見表7,當j等于,發生不匹配時,因next[5]=4,則需將j回溯到4進行比較;又因next[4]=3,則應將j回溯到3進行比較…由此可見,j需要依次在5、4、3、2、1的位置上進行比較,而模式串在1到5的位置上的字符完全相等,因此較為聰明的做法應該是在j等于5處發生不匹配時,直接跳過位置1到4的多余比較,這就是KMP演算法改進的切入點,

? 將上述程序推廣到一般情況為:
? 若Pj等于Pk1(k1=next[j]),則繼續比較Pj與Pk2(k2=next[next[j]]),若仍相等則繼續比較下去,直到Pj與Pkn不等(kn=next[next[next[j]…]],嵌套n個next)或kn等于0時,則next[j]重置為kn,一般保持next陣列不變,而用名為 nextval的陣列來保存更新后的next陣列,即當Pj與Pkn不等時, nextval[j]賦值為kn,
? 下面通過一個例題來看一下 nextval的推導程序,
? 【例】求模 ABABAAB式串的next陣列和 nextval陣列,
? 首先求出next陣列,見表8,

? 1)當j為1時,nextval[1]賦值為0,特殊情況標記,
? 2)當j為2時,P2為B,Pk1(k1=next[2],值為1)為A,兩者不等,因此 nextval[2]賦值為1,
? 3)當j為3時,P3為A,Pk1(k1=next[3],值為1)為A,兩者相等,因此應先判斷k2是否為0,而k2等于next[next[3]],值為0,所以 nextval[3]賦值為k2,值為0,
? 注意:步驟3)中P3與Pk1(k1=next[3])比較相等后,按照之前的分析應先判斷k2是否為0,再讓P3繼續與Pk2比較,注意到此時 nextval[next[3]]即 nextval[1]的值已經存在,故只需直接將 nextval[3]直接賦值為 nextval[1]即可,即 nextval[3]=nextval[3]=0,
? 推廣到一般情況為:當Pj等于Pk1(k1=next[j])時,只需讓 nextval[j]賦值為 nextval[next[j]]即可,原因有兩點:
? ① nextval陣列是從下標1開始逐漸往后求得的,所以在求 nextval[j]時, nextval[next[j]]必已求得,
? ② nextval[next[j]]為Pj與Pk2到Pkn比較結果的記錄,因此無須再重復比較,
? 4)當j為4時,P4為B,Pk(k=next[4])為B,兩者相等,因此 nextval[4]賦值為 nextval[next[4]]值為1,

? 5)當j為5時,P5為A,Pk(k=next[5])為A,兩者相等,因此nextval[5]賦值為nextval[next[5]],值為0,

? 6)當j為6時,P6為A,Pk(k=next[6])為B,兩者不等,因此nextval[6]賦值為next[6],值為4,

? 7)當j為7時,P7為B,Pk(k=next[7])為B,兩者相等,因此nextval[7]賦值為nextval[next[7]],值為1,

? 由此求得nextval陣列見表9

? 總結求nextval的一般步驟:

? 1)當j等于1時,nextval[j]賦值為0,作特殊標記,

? 2)當Pj不等于Pk時(k=next[j]),nextval[j]賦值為k,

? 3)當Pj等于Pk時(k=next[j]),nextval[j]賦值為nextval[k],

? 求next陣列的函式getnext()的核心代碼段:

if(j == 0 || substr.ch[i] == substr.ch[j])
{
	++i;
	++j;
	next[i] = j;//1
}
else
	j = next[j];//2

? 在注釋1處next[i]已求出,且next[0...i-1]皆已求出,則結合上邊的總結,要求nextval,可以在1處添加以下代碼

next[i] = j;//1:i處不匹配,應跳回j處
if(substr.ch[i] != substr.ch[next[i]])
	nextval[i] = next[i];
else
	nextval[i] = nextval[next[i]];

? 顯然,在注釋2處用next陣列來回溯j的代碼可以用已求得的nextval陣列代替(注意,j往前跳,之前的nextval值已經求得),修改后的代碼如下:

j = nextval[j];//2

? 通過以上的分析,可以將函式的getnext()中的next陣列用nextval陣列代替掉,最終得到求nextval的代碼:

void getnextval(Str substr,int nextval[])
{
	int i = 1,j = 0;//串從陣列下標1位置開始儲存,因此初值為1
	nextval[1] = 0;
	while(i < substr.length)
    {
		if(j == 0 || substr.ch[i] == substr.ch[j])
        {
			++i;
			++j;
			if(substr.ch[i] != substr.ch[j])
				nextval[i] = j;
			else
			 nextval[i] = nextval[j];
		}
		else
			j = nextval[j];
	}
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/5289.html

標籤:其他

上一篇:leetcode刷題筆記-3. 無重復字符的最長子串(java實作)

下一篇:UVa439(騎士的移動)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more