(原創)數據結構之利用KMP算法解決串的模式匹配問題


 

 

 

給定一個主串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 }

 





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM