1. 程式人生 > 實用技巧 >KMP(烤饃片)演算法

KMP(烤饃片)演算法

啥是烤饃片

烤饃片就是好吃的
KMP就是對於暴力求一個字串是否是另一個字串的子串的優化

演算法思想 & 例題

  • 給出兩個字串S1,S2,要求判斷S2是否為S1子串
  • 首先先來想想暴力的思想,很容易我們就會想到暴力列舉S1中每一個點作為起點,如果向後延伸S2長度中所有點都與S2相同,則證明含有,否則列舉下個點
  • 但是這是nm的效率 顯然對於一些大資料我們是過不去的
  • 所以烤饃片就此誕生,它可以做到線性的效率
  • 也許你們會更快,但我雜湊永遠不慢
  • 首先我們進行預處理 預處理出一個fail陣列 fail陣列記錄啥呢? fail[i]表示在a[1..i]中的最長公共前後綴(不能為自己)
    舉個栗子:
     a: a b a b a b a a c
    fail:{0,0,1,2,3,4,5,1,0}
  • 假定你已經知道fail陣列咋求,那麼fail陣列有什麼用呢?
    對於這樣一個序列 a b a b a b a a c
    問是否存在a b a b a a c這樣一個子串
    顯然它是存在的 如果我們要暴力求解的話,先列舉第一個a 然後列舉到第六個位置的時候發現不匹配 然後重新來
    這樣一個過程顯然是很浪費時間的 因為我們已經知道從第一個到第四個其實是匹配的 那我們可以直接在不匹配的時候將下面這個匹配串開頭放到第三個字元a的位置
    那麼這個放到哪個位置怎麼去確定呢?
    沒錯就是我們的fail 如果當前位置失配了 沒必要從頭再列舉一遍 而是直接從失配那個地方開始列舉
  • 所以fail到底怎麼求呢?
    假設已經求出了fail[1…6],正在求fail[7]。
    發現 A[fail[6]+1]A[7],那麼fail[7]=fail[6]+1,且一定是最優的。
    接下來求fail[8],
    發現A[fail[7]+1]!=A[8],說明接下來我們要考慮次優解(即判斷以7為結尾的次長公共前後綴能否向後繼續擴充套件),由fail陣列的定義得出,以7為結尾的次長公共前後綴長度為fail[fail[7]],如果再次失配(即A[fail[fail[7]]+1]!=A[8]),則需要繼續跳fail,直到匹配上(出現了A[j+1]
    A[8],此時fail[8]=fail[j]+1)或者fail已經為0且仍舊失配。
    例如上面A串最終有A[8]==A[0+1],所以fail[8]=fail[0]+1=1。
    一共只有上面兩種情況,程式碼實現是比較簡單的。
  • 所以看程式碼實現吧
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e7;
char s1[maxn],s2[maxn];
int fail[maxn];

int main(){
	scanf("%s%s",s1+1,s2+1);
	int len1 = strlen(s1 + 1);
	int len2 = strlen(s2 + 1);
	for(int i = 2,j = 0;i <= len2;++i){
		while(j && s2[i] != s2[j + 1])j = fail[j];
		if(s2[i] == s2[j+1])fail[i] = ++j;
	}
	for(int i = 1,j = 0;i <= len1;++i){
		while(j && s1[i] != s2[j + 1])j = fail[j];
		if(s1[i] == s2[j + 1])j++;
		if(j == len2){printf("Yes\n");return 0;}
	}
	printf("No\n");
	return 0;
}