UVALive 3026 Period (KMP算法简介)
kmp的代碼很短,但是不太容易理解,還是先說明一下這個算法過程吧。
樸素的字符串匹配大家都懂,但是效率不高,原因在哪里?
匹配過程沒有充分利用已經匹配好的模版的信息,比如說,
i是文本串當前字符的下標,j是要匹配的模版串當前正在匹配的字符的下標。(下標都從零開始,j同時可以表示已經匹配的字符長度)
當匹配到i = 4, j = 4的時候失配了,樸素的匹配做法是往右邊移一位然后從j開始掃,這樣做效率很低。
不難發現前面已經匹配好的串ab是最長公共前綴后綴。把串移動到后綴的第一個位置正好是
樸素的匹配過程中第一次匹配能把這個前綴匹配匹配完的位置!因此令j = f[j-1] = 2。
模版串下面這個f數組又是怎么來的呢?
尋找最長公共前綴后綴過程本質上也是一個匹配。
i表示當前要求f[i]的下標,j表示之前串已經匹配的最大前綴后綴長度。(j = -1表示0號位置也沒有匹配上)
當i = 6時,之前已經匹配了j個,所以只要i只要從j下標位置開始比較就行了。當匹配的時候f[i] = j+1。
如果不匹配,利用前面已經得到的f值進行匹配。
當i = 7, j = 3時,發生失配,利用之前的f值,可以知道a是最長的前綴和后綴,那么只需要從a開始匹配,因此令j = f[j-1],
重復上述過程直到匹配或j = -1的時候為止。當前就等于: f[i] = j+1。(j=-1表示沒有匹配也是為了方便統一處理)
在代碼中,因為當j = 0的時候,j = f[j-1]不好表示,因此把整個f數組向右邊移動一位。此時只需把上述過程的j = f[j-1]替換成j = f[j]。
----------------------------------------------分割線----------------------------------------------------------------
這道題要求前綴的最小循環周期。
先構造一個循環的串,然后進行求失配函數f的過程,當第一次前綴長度等于后綴長度并且各占一半的時候,這個前綴一定是最小的循環節。(從第一個結論開始用歸納法證明)
可以歸納出一個結論:在每個循環節終止的位置 i-f[i] ==最短循環節長度。
反過來i能被(i-f[i])整除可以推出i-f[i]是最短循環節。(同樣用歸納法)
?
kmp之前不太理解,只會套。為學習ac自動機,先把的kmp基礎打好。
?
#include<bits/stdc++.h> using namespace std; const int maxn = 1e6+5; int f[maxn]; char str[maxn];void getF(char *s) {f[0] = -1;for(int i = 0, j = -1; s[i]; ){while(~j && s[i] != s[j]) j = f[j];f[++i] = ++j;} }int main() {//freopen("in.txt","r",stdin);int n,kas = 0;while(scanf("%d\n",&n),n){gets(str);getF(str);printf("Test case #%d\n",++kas);for(int i = 2; i <= n; i++){if(f[i] && i%(i-f[i]) == 0)printf("%d %d\n",i,i/(i-f[i]));}putchar('\n');}return 0; }?
轉載于:https://www.cnblogs.com/jerryRey/p/4794854.html
總結
以上是生活随笔為你收集整理的UVALive 3026 Period (KMP算法简介)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: xpool, cpool,epoo
- 下一篇: LeetCode 117. Popula