扩展kmp算法总结

扩展kmp是用来处理这样一类问题的:有一个文本串S,一个模式串T,求对于S串每一个后缀与T串的最长公共前缀,有学过后缀数组的同学可能会发现,完全可以把两串连接起来然后求lcp,复杂度为O(nlogn),但是这个算法的时间复杂度和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 <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
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("%lld\n", ans);	
	ans = extend[0]+1;
	for(int i = 1; i < n; i++)
		ans = ans^((i+1)*((long long)extend[i]+1));
	printf("%lld\n", ans);
    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值