KMP
字元串比對問題
得到失配指針,fail的值為上一次比對到的最長長度-1,因為是公共字首,是以也就是下标-1
得到上一次比對位置j後,目前位置與j+1位置 字母相同則比對長度+1,否則無法比對,即為-1
a | a | b | a | b | a | a | b |
---|---|---|---|---|---|---|---|
fail | -1 | -1 | -1 | 1 | 2 |
void getFail(char* ptr, int *fail){
int m = strlen(ptr), j = -1;
fail[0] = -1;
for(int i=1; i<m; ++i){
while(j!=-1 && ptr[i]!=ptr[j+1])
j = fail[j];
fail[i] = ptr[i]==ptr[j+1]? ++j: -1;
}
}
比對
類似fail數組的獲得過程,j為上一次比對到的位置,也就是目前ptr與str已比對長度
如果目前str的位置i與ptr的位置j的下一位比對,則長度+1,即 ++ j,長度滿足m-1,比對成功
void find(char* str, char* ptr, int* fail){
int n = strlen(str), m = strlen(ptr);
getFail(ptr, fail);
int j = -1;
for(int i=0; i<n; ++i){
while(j!=-1 && str[i]!=ptr[j+1])
j = fail[j];
if(str[i] == ptr[j+1]) ++ j;
if(j == m-1)
printf("%d\n", i-m+2);
}
}
拓展KMP
在O(|S| + |T|)的時間複雜度上,計算出文本串str的所有字尾與模式串ptr的最長公共字首,類似manacher
一篇優秀的文章:劉毅-拓展kmp
getNext: next[ ] 為ptr的字尾與ptr的公共字首
getExtend:extend[ ] 為str的字尾與ptr的公共字首
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|---|
S | a | a | a | a | a | b | b | b |
T | a | a | a | a | a | c | ||
extend[i] | 5 | 4 | 3 | 2 | 1 |
獲得extend:不了解看劉毅裡面的圖
- 如果
,此時i + next[i - a] < p
。extend[i] = next[i - a]
-
i + next[i - a] == p,
有可能等于S[p]
,是以我們可以直接從T[p - i]
與S[p]
開始往後比對,加快了速度。T[p - i]
-
i + next[i - a] > p,
,是以就沒有繼續往下判斷的必要了,我們可以直接将S[p] != T[p - i]
=extend[i]
。p - i
void getNext(char* ptr, int* next){
int p = 0, st = 0; //st為達到最遠位置p的起始點
int m = strlen(ptr);
next[0] = m;
for(int i=1; i<m; ++i){
if(i >= p || i + next[i-st] >= p){
if(i >= p)
p = i; //超過即重新比對
while(p < m && ptr[i] == ptr[p - i])
++ p;
next[i] = p - i;
st = i;
}
else
next[i] = next[i - st];
}
}
void getExtend(char* str, char* ptr, int* next, int* extend){
int n = strlen(str), m = strlen(ptr);
getNext(ptr, next);
int st = 0, p = 0;
for(int i=0; i<n; ++i){
if(i >= p || i + next[i-st] >= p){
if(i >= p)
p = i;
while(i < n && p-i < m && str[i] == ptr[p - i])
++ p;
extend[i] = p - i;
st = i;
}
else
extend[i] = extend[i - st];
}
}