初识扩展 KMP , 看了挺久的。可以参考这篇博客 和 第二篇
给定两个字符串 S , T (序列),求( S 的所有后缀 )和 T 的最长前缀。
例如 S 是 abcde
求 abcde 和 T 的最长前缀
求 bcde 和 T 的最长前缀
求 cde 和 T 的最长前缀
求 de 和 T 的最长前缀
求 e 和 T 的最长前缀
如果最长前缀长度 = T.length() ,就是 KMP 了。
我个人脑海里抽象的 KMP 就如下图所示:
如果我知道了 p 处的S后缀和 T 的最长前缀是 extend[p] , 要求 i 处S的后缀和 T 的最长前缀。
可以发现 , 从 [ p , p+extend[p] ] 和 T 的 [ 0 , extend[p] ] 是一模一样的,如果 i 处在 这段相同的内部,就可以得出 1 和 2 是一样的,然后,我利用类似 KMP 的“一头一尾”前缀后缀,就可以得出 2 和 3 有一部分(next[i-p])是一样的, 这样就可以让从 i 处开始的S后缀,直接跳 " next[i-p] " 个距离,再继续匹配。
的确很抽象,整体就是 1 == 2 , 根据 T 自身的KMP , 找出 2 和 3 相同的部分 ,最终 S 直接 从 i+ next[i-p] 之后开始匹配
这样就可以省去一部分的匹配,T 从 next[i-p] 之后继续匹配。
然后就是一些分类了,分类可以参考这篇博客 https://www.61mon.com/index.php/archives/186/
#include <bits/stdc++.h>
using namespace std ;
const int N = 1000005 ;
char S[N<<1|1] , T[N] ;
int next[N] , extend[N<<1|1] ;
int begin ;
void Extend_KMP( char *S , char *T , int *extend , int *next ){
int n = strlen( S ) , m = strlen( T ) ;
int p = 0 , mx = 0 ;
for( int i = begin ; i < n ; ++i ){
if( i + next[i - p] < mx ) // mx = p + extend[p] 是最远的右端
extend[i] = next[i - p] ;
else{
int j = max( 0 , mx - i ) ;
while( i+j < n && j < m && S[i + j] == T[j] )
++j ;
mx = ( p = i ) + ( extend[i] = j ) ;
}
}
}
int main(){
while( ~scanf( "%s%s" , S , T ) ){
cout << S << endl << T << endl ;
next[0] = strlen( T ) ;
begin = 1 ;
Extend_KMP( T , T , next , next ) ;
begin = 0 ;
Extend_KMP( S , T , extend , next ) ;
}
return 0 ;
}
如果代码有错,敬请指正。