『筆記』KMP算法 字符串匹配(看貓片)

前言

此篇筆記根據本身的理解和練習心得來解釋算法,只表明我的觀點,若有不足請指出(我剛學QWQ)ios

淺談字符串匹配

設想一個場景,假設你是一個淨化網絡語言環境的管理員,天天須要翻閱大量的文章和帖子來查找敏感字,那麼這個時候很簡單的能夠想到能夠運用字符串匹配來作到,可是樸素的單模式字符串匹配耗用時間多,那麼在這裏咱們就能夠運用高效率的\(KMP\)算法來解決。算法

算法對比

  • 樸素單模式字符串匹配算法

樸素的單模式串匹配大概就是枚舉每個文本串元素,而後從這一位開始不斷向後比較,每次比較失敗以後都要從頭開始從新比對
給定一個文本串(要查找符合條件的字符串),以及一個模式串(須要匹配的字符串)數組

模式串:abcab
文本串:abcacababcab

通常的思想就是,當咱們在第五位失配時,咱們會從當前模式串的第一位所處在文本串的位置的後一位開始與模式串的第一位進行匹配,直到匹配成功位置,就會出現如下的狀況網絡

模式串:   abcab
文本串:abcacababcab

這一種算法的正確性是100%的,可是複雜度顯然不是那麼喜人,通常來講,指望時間複雜度能夠爲\(O(n+m)\)的,可是一些有心的出題人甚至能夠卡到\(O(nm)\),因此這個算法僅僅用於暴力打法便可。spa

  • \(KMP\)字符匹配算法

對於失配之後的字符串,不須要去從頭開始枚舉浪費時間,而是根據預先處理好的值來進行枚舉便可,也就是尋找最優歷史處理,
根據處理的過程,時間複雜度爲\(O(n+m)\)code

依舊是給定一個樣例來講明ci

模式串:abcabc
文本串:abcabdababcabc

在這個樣例中咱們能夠看到,在第六位的時候失配了,這個時候有一個\(KMP\)算法的專門小\(trick\),咱們觀察字符串能夠發現的是,在模式串中,失配的前一位第五位在這整個模式串中能夠找到一個和他相匹配的字符,也就是第二位的\(b\),那麼咱們如今就能夠把模式串的匹配長度跳轉到\(2\)繼續查找,那麼爲何能夠這樣作呢,由於咱們能夠發現,在匹配的時候,第五位的\(b\)之因此能夠轉移到第二位,靠的就是以第五位結尾的一個子串,能夠與從\(1-2\)這個範圍內的子串一一對應起來,也就是徹底的按位匹配。字符串

那麼轉移過來就是這樣的string

模式串:   abcabc
文本串:abcabdababcabc

經過這個方法咱們能夠節省一大堆無用的時間。it

思路分析

首先設置\(kmp\)數組爲失配數組,也就是存儲當匹配失敗後跳轉到接下來模式串匹配的最優的位置,由於相比較於文本串,模式串更加的靈活多變,處理起來也很方便,那麼處理失配數組的時候將模式串當作處理串。

那麼核心就在於如何來處理失配數組的值

咱們要明確的是,在上面條件的基礎上,咱們要考慮的是當模式串的第\(i\)爲失配之後,如何來調到最優的位置繼續進行匹配,由於在文本串當中\(i\)之前的位置都已經失效了,那麼咱們對於每個\(kmp_i\)要記錄的是:

在模式串\(str\)中,咱們能夠找到一個最優的位置\(j\),知足\(i\geq j\)而且知足\(str_i=str_j\),而且在\(j!=1\)的時候,有從\(str_1-str_{j-1}\)分別與\(str_{i-j+1}-str_{j-1}\)按位匹配。

簡單來講就是在模式串中,存在一個長度爲\(len\)的以\(1\)開頭以\(j\)結尾的子串與以\(i-j+1\)開頭以\(i\)結尾的子串徹底相同

代碼實現

#include<iostream>
#include<cstring>
#include<stack>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<queue>
#include<map> 
using namespace std;
const int N=1e6+9; 
int kmp[N],j;
int lena,lenb;
char a[N],b[N]; 
int main()
{
	cin>>a+1;
	cin>>b+1;
	lena=strlen(a+1);
	lenb=strlen(b+1);
	//預處理kmp數組 
	for(int i=2;i<=lenb;i++)//第一個字符必定是匹配的因此不須要i=1 
	{
		while(b[i]!=b[j+1]&&j)//若是不匹配就往回跳 
		j=kmp[j];
		if(b[i]==b[j+1])j++;//碰見相同的就向右移動 
		kmp[i]=j; 
	} 
	j=0;
	for(int i=1;i<=lena;i++)
	{
		while(j&&b[j+1]!=a[i])
		j=kmp[j];//經過本身匹配本身來求得每個點的kmp值 
		if(b[j+1]==a[i])
		j++;
		if(j==lenb)
		{
			printf("%d\n",i-lenb+1);//這個是開始的值 
			j=kmp[j]; 
		}
	}
	for(int i=1;i<=lenb;i++)
	printf("%d ",kmp[i]);
	return 0; 
}
相關文章
相關標籤/搜索