扩展kmp是用来处理这样一类问题的:有一个文本串S,一个模式串T,求对于S串每一个后缀与T串的最长公共前缀。这个算法的时间复杂度和kmp一样,都是O(n)的。最终答案保存于extend[i]中,extend[i]表示S串从i开始的后缀与T串最长公共前缀长度。如果遍历extend数组,统计extend[i]等于strlen(T)的出现次数,那它就实现了kmp的功能,因此称其为扩展kmp算法。
以P5410 【模板】扩展 KMP(Z 函数) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)这道题为例,介绍一下模板。
在求extend数组之前,需要一个辅助数组_next,就像kmp算法一样。不过这里的_next数组含义与kmp中不同,_next[i]表示T串从i开始的后缀与T串最长公共前缀长度,比较一下之前extend数组含义,可以发现求_next数组的过程不过是自己和自己匹配了一遍,于是只需要写正确求extend数组那部分代码,就可以复制一遍求_next数组了,就像kmp算法中求_next一样。
核心思想其实是和manacher算法很接近的,都是记录一下当前已求出的最靠右的区间,根据某个数组得到当前位置已经匹配成功的长度,如果小于区间右端点就直接记录答案,如果大于右端点就暴力匹配。
代码细节可以参考这篇博客:扩展 KMP 算法 - 刘毅的个人博客 (ethsonliu.com)
#include#include #include #include using namespace std; //以洛谷P5410为例 char S[20000010], T[20000010]; int n, m, _next[20000010], extend[20000010]; //求_next[] void GetNext() { int l = 0, r = 0; _next[0] = m; for (int i = 1; i < m; i++) { if (i >= r || i + _next[i-l] >= r) { if (i >= r) r = i; while (r < m && T[r] == T[r-i]) r++; _next[i] = r-i; l = i; } else _next[i] = _next[i-l]; } } //求extend[] void GetExtend() { int l = 0, r = 0; GetNext(); for (int i = 0; i < n; i++) { if (i >= r || i+_next[i-l]-1 >= r-1)// i>=p的作用:举个典型例子,S和T无一字符相同 { if (i >= r) r = i; while (r < n && r-i < m && S[r] == T[r-i])//暴力匹配 r++; extend[i] = r-i; l = i; } else//根据next数组定义 extend[i] = _next[i-l]; } } signed main() { cin >> S >> T; n = strlen(S), m = strlen(T); GetExtend(); long long ans = _next[0]+1; for(int i = 1; i < m; i++) ans = ans^((i+1)*((long long)_next[i]+1)); printf("%lldn", ans); ans = extend[0]+1; for(int i = 1; i < n; i++) ans = ans^((i+1)*((long long)extend[i]+1)); printf("%lldn", ans); return 0; }



