KMP小擴展,找出子串在主串中出現的所有位置


KMP算法能夠高效地匹配字符串,找出子串(T串)在主串(S串)中出現的首個位置的原算法網上已經有很多優秀的博文進行詳細講解,這里就不多贅述。

這篇博文主要是對KMP原算法稍作改動,使其能夠在主串中把所有匹配的主串找出來。


找出首個匹配的算法好弄,next數組求出來后直接用來匹配,直到出現完全匹配的情況的時候就停止搜索把答案扔出來就行,但是想把所有T串找出來的話就得完全把S串搜完, 就算已經在S串中找到一個T串后也是不能馬上停止搜索的。

難點就在已經完全匹配了一個T串以后怎么繼續進行下一個匹配。

完全匹配T串后,我們需要將S串的i指針往下挪一位,那么容易知道前面的字符串都已經是匹配過的了,根據KMP算法的思想,我們需要將T串的j指針進行回溯就能繼續匹配,問題就在於這個j指針應該回溯到哪里才合適。

這里給出一個例子

主串(S串) ababaab

子串(T串) aba

這里的下標就從0開始吧,我們看到T串在S串中出現的位置是0和2,現在我們先把第一次匹配的情況畫出來,這里next數組也順便給出來了,這是沒優化過的next數組

然后根據原KMP算法,i++, j++

按照原算法,在下個循環當中應該已經退出循環了,但是在這里我們當然不能這么做,所以我們應該將j回溯,不過這問題來了,之前我們進行回溯是根據next數組來將j進行回溯的,在上圖的情況下,我們可以看到j所指的位置並沒有對應的next值,那怎么辦?

先不討論算法,就單純觀察我們知道上圖中的j應該是回溯到了b(T串1位置)那里,因為T串末尾的a跟首部的a相同,也就是這部分的后綴分前綴是相同的,根據KMP算法思想,這部分匹配過,就應該回溯到相同前綴的后一位,這就回溯到了b

到這里我們就已經是發現了,其實這種回溯就是在把next數組多往后求一位,雖然傳統的KMP算法在求next數組時只是求出跟T串等長長度而已,但其實多往后求一位也是可以的,我們回到求T串求到最后一位時的情景:

指針的情況在如上圖所示時,按傳統算法本該退出循環的,但是理論上確實還是可以再往后求一位的,這里又有T[i] == T[j],所以i++, j++,然后next[i] = j,這樣,我們就又額外得到了一位next數組的值

這個最后一位的next值意義是一樣的,我們可以把這個字符串看成是長度為4的字符串,然后這個T[3]字符非常詭異,任何字符都不與它相等,也就是說,這個位置是必定匹配失敗的,但是由於前3位字符都匹配成功了,所以這個回溯依然是合理的。

這樣一來,S串中找出多個T串的算法就好弄了,這個T串我們看成是原先T串上多延長了一位,但是這個最后一位怎么匹配也不會匹配成功,所以在這個匹配算法中最多只會匹配到原T串長度,然后匹配到T串+1長度時就會回溯j指針,通過這種方法,我們就可以找出所有的T串出現位置了。


 

附代碼(next數組優化過的那種):

 1 vector<int> KMP(const string& S, const string& T)
 2 {
 3     vector<int> Next;
 4     Next.push_back(-1);
 5 
 6     for (int i = 0, j = -1; i < T.size();) {
 7         if (j == -1 || T[i] == T[j]) {
 8             i++, j++;
 9             if (i != T.size() && T[j] == T[i]) Next.push_back(Next[j]);
10             else Next.push_back(j);
11         }
12         else j = Next[j];
13     }
14 
15     vector<int> res;
16     for (int i = 0, j = 0; i < S.size() && j < (int)T.size();) {
17         if (j == -1 || S[i] == T[j]) {
18             i++, j++;
19             if (j == T.size()) {
20                 res.push_back(i - j);
21                 j = Next[j];
22             }
23         }
24         else j = Next[j];
25     }
26 
27     return res;
28 }

 


免責聲明!

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



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