給定一個主串S(長度<=10^6)和一個模式T(長度<=10^5),要求在主串S中找出與模式T相匹配的子串,返回相匹配的子串中的第一個字符在主串S中出現的位置。
輸入格式:
輸入有兩行: 第一行是主串S; 第二行是模式T.
輸出格式:
輸出相匹配的子串中的第一個字符在主串S中出現的位置。若匹配失敗,輸出0.
輸入樣例:
在這里給出一組輸入。例如:
aaaaaba
ba
輸出樣例:
在這里給出相應的輸出。例如:
6
解題思路:串的模式匹配有兩種:一種是BF算法,一種是KMP算法;
基於這道題給的數據,若用BF算法便會超時,所以我們這道題用KMP算法;
那么問題來了,KMP算法到底怎么用的;簡單來講,就是有兩個步驟:
1、求模式串的next數組;
2、進行主串與模式串的匹配;
假設主串和模式串分別為
第一個問題:如何求next數組
🔺next數組求的是模式串的!!!
下面就以上面給的模式串為例;
next數組便是前綴中的最長相同前后綴,說起來比較繞,什么意思呢,模擬一遍就清楚了;

所以對於模式串對應的next數組為

這樣我們就求出了next數組;
接下來進行模式匹配,其實這樣就會有個問題,所以實際上next數組這樣是需要改進的;
假設我們不改進的話,進行匹配會出現什么問題呢;
進行模式匹配的大概代碼如下:
1、即匹配,則i++;j++;
2、不匹配,根據剛剛求出的next數組,進行跳next數組;
下面代碼中ssize為主串s的長度,tsize為模式串t的長度;
下面我們就根據代碼模擬一遍;
1 2 int i = 0 ; 3 int j = 0; 4 while(i<ssize&&j<tsize) 5 { 6 7 if(s[i]==t[j]) 8 { 9 i++; 10 j++; 11 12 } 13 else 14 { 15 j = next1[j]; 16 } 17 18 19 } 20 21 22 if(j==tsize) 23 { 24 cout << i-j+1; 25 }
上面我們求出來的next數組為:
現在我們把它們的下面也寫上:
現在開始模擬一遍:
我們發現匹配到c的時候不匹配了,跳next數組,則跳到下標為0處,變成:

此時也不匹配,變成應該跳next數組,跳到下標為0處,但是這樣就變成死循環了,所以我們應該退一步,將next數組的第0個賦值為-1,且將整個next數組向后移;就不會變成死循環了;
再模擬一次:
此時不匹配跳next數組;
變成:
發現a不匹配,跳next數組:
繼續模擬:
發現不匹配,所以此時next應該跳到下標為-1處,但是這里沒有下標為-1的,所以實際上就是整體向后移;變成:
發現完全匹配了;
那么基於上面的改進,next數組應該怎么寫呢:
代碼如下:
1 string s; 2 string t; 3 int ssize; 4 int tsize; 5 int next1[2000000]; 6 void nextsz(string t,int tsize) 7 { 8 next1[0] = -1; //防止進入死循環,而且到不能匹配時能整體后移 9 int k = -1; //是為了調節next數組; 10 int j = 0 ; 11 while(j < tsize-1) 12 { 13 if(k==-1||t[j]==t[k]) //k=-1進入這個循環是為了整體向后移; 14 { 15 ++k; k實際上也是記錄了相同的個數; 16 ++j; 17 18 next1[j] = k; 找到next數組; 19 20 } 21 else 22 k = next1[k]; //不相同則更新k; 23 } 24 25 }
現在會了next數組,我們則可以進行模式匹配了;
利用上面求的next數組來進行模式匹配;過程原理和上面畫的圖是一模一樣的;
代碼如下:
1 int kmp(string s,string t,int sszie,int tsize) 2 { 3 int j = 0; 4 int i = 0; 5 while(i<ssize&&j<tsize) 6 { 7 8 if(j==-1||s[i]==t[j]) j=-1是為了調節到跳無可跳時,整體向后移; 9 { 10 i++; //匹配整體向前移; 11 j++; 12 13 } 14 else 15 { 16 j = next1[j]; 不斷跳next數組; 17 } 18 19 20 } 21 22 23 if(j==tsize) 24 { 25 return i-j+1; //返回模式串在主串的第一個下標; 26 } 27 else return -1; //不匹配,則返回-1; 28 }
所以這道題的完整代碼如下:
代碼如下:
1 #include<iostream> 2 #include<string.h> 3 using namespace std ; 4 5 string s; 6 string t; 7 int ssize; 8 int tsize; 9 int next1[2000000]; 10 void nextsz(string t,int tsize) 11 { 12 next1[0] = -1; 13 int k = -1; 14 int j = 0 ; 15 while(j < tsize-1) 16 { 17 if(k==-1||t[j]==t[k]) 18 { 19 ++k; 20 ++j; 21 22 next1[j] = k; 23 24 } 25 else 26 k = next1[k]; 27 } 28 29 } 30 31 int kmp(string s,string t,int sszie,int tsize) 32 { 33 int j = 0; 34 int i = 0; 35 while(i<ssize&&j<tsize) 36 { 37 38 if(j==-1||s[i]==t[j]) 39 { 40 i++; 41 j++; 42 43 } 44 else 45 { 46 j = next1[j]; 47 } 48 49 50 } 51 52 53 if(j==tsize) 54 { 55 return i-j+1; 56 } 57 else return 0; 58 } 59 60 61 int main() 62 { 63 cin>>s; 64 cin>>t; 65 66 ssize = s.size(); 67 tsize = t.size(); 68 nextsz(t,tsize); 69 cout<<kmp(s,t,ssize,tsize)<<endl; 70 71 72 73 }