一些约定:

  • 字符串下标从1开始
  • s[1,i]表示S的第一个到第i个字符组成的字符串

解决的题型:

给你两个字符串A,B(A.size()=n,B.size()=m),求p数组

p[i]表示最大的len使得A[i,i+len-1]=B[1,len]

即A的第i位与B前缀的最大匹配的长度

比如;

A:aaaabaa

B:aaaa

p数组就是{4 3 2 1 0 2 1}

算法实现

  1. 我们要先求一个关于B的z数组,一些定义:
  • z[i]表示最大的len使得B[1,len]=B[i,i+len-1],比如对于B:aabcaabd z[5]=3, 一开始z[1]=m

  • Z-box:表示一段区间,用两个变量l,r表示,它表示目前为止B中右端点最大的一个区间[l,r]满足,B[l,r]=B[1,r-l+1] (即这段区间与它等长度的前缀相同),一开始l=r=0,在循环求解z数组的过程中不断更新

知道了一些定义我们就来看z数组怎么求

假设我们已经得出z[1]~z[i-1]了要求z[i],且目前的Z-box=[l,r]

(1) \(l \le i \le r\) 如下图

(实际上如果\(i \le r\) , i一定满足\(l \le i \le r\) ,因为我们是用1~i-1去更新l的)

r’表示r-l+1,则B[1,r’]=B[l,r] (为了下面叙述方便,我们称r’为r的对应点) 相同颜色代表这段区间相同

我们再求出i的对应点i’=i-l+1,则B[i’,r’]=B[i,r]

假设z[i’]=len 则B[1,len]=B[i’,i’+len-1],现在有两种情况

  • \(len \le r-i+1\):此时\(B[1,len]=B[i’,i’+len-1]=B[i,i+len-1]\)

    则z[i]=len

  • 而如果 \(len>r-i+1\),如下图

我们无法确定绿色部分是否相同,因此不能直接把len赋给z[i],但我们可以保证z[i]>=r-i+1,r后面的部分暴力扫描即可

(2)\(i>r\) 同样也是暴力往后扫描即可

注意:每次求完z[i]后如果i+z[i]-1>r 则用i和i+z[i]-1更新l,r

求z数组的代码如下

	z[1]=m,l=0,r=0;
for(int i=2;i<=m;i++)
{
if(i<=r) z[i]=min(z[i-l+1],r-i+1);
while(b[i+z[i]]==b[1+z[i]]) z[i]++; //如果i>r那这里z[i]一开始是0
if(i+z[i]-1>r) r=i+z[i]-1,l=i;
}

2.求解p数组过程差不多

这里的[l,r]表示最大的一段区间满足a[l,r]=b[1,r-l+1]

一开始,l=0,r=0

外层循环i从1到n (这里p[1]不用赋初值)

(1) \(l \le i \le r\) , 令i’=i-l+1,r’=r-l+1,len=z[i']:

  • ① \(len \le r-i+1\),则B[1,len]=B[i’,i’+len-1]=A[i,i+len-1],所以p[i]=len

  • \(len>r-i+1\),超出部分暴力扫描

(2) \(i>r\),暴力扫描

记得更新l,r

求解p数组代码

	l=0,r=0;
for(int i=1;i<=n;i++)
{
if(i<=r) p[i]=min(z[i-l+1],r-i+1);
while(a[i+p[i]]==b[1+p[i]]&&i+p[i]<=n&&1+p[i]<=m) p[i]++;
//注意这里要判断边界
if(i+p[i]-1>r) r=i+p[i]-1,l=i;
}

最后放上一道板题

P5410 【模板】扩展 KMP/exKMP(Z 函数)

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e7+5;
inline int read(){
int w = 1, s = 0;
char c = getchar();
for (; c < '0' || c > '9'; w *= (c == '-') ? -1 : 1, c = getchar());
for (; c >= '0' && c <= '9'; s = 10 * s + (c - '0'), c = getchar());
return s * w;
}
char a[N],b[N];
int n,m;
string s1,s2;
int z[N],p[N];
int l,r;
int ans1,ans2;
signed main()
{
cin>>s1>>s2;
n=s1.size(),m=s2.size();
for(int i=1;i<=n;i++) a[i]=s1[i-1];
for(int i=1;i<=m;i++) b[i]=s2[i-1];
z[1]=m,l=0,r=0;
for(int i=2;i<=m;i++){
if(i<=r) z[i]=min(z[i-l+1],r-i+1);
while(b[i+z[i]]==b[1+z[i]]) z[i]++;
if(i+z[i]-1>r) r=i+z[i]-1,l=i;
}
for(int i=1;i<=m;i++){
ans1^=(z[i]+1)*i;
}
l=0,r=0;
for(int i=1;i<=n;i++){
if(i<=r) p[i]=min(z[i-l+1],r-i+1);
while(a[i+p[i]]==b[1+p[i]]&&i+p[i]<=n&&1+p[i]<=m) p[i]++;
//注意这里要判断边界
if(i+p[i]-1>r) r=i+p[i]-1,l=i;
}
for(int i=1;i<=n;i++){
ans2^=(p[i]+1)*i;
}
printf("%lld\n%lld\n",ans1,ans2);
return 0;
}

本博客参考的网址:

[C++]洛谷:【模板】扩展 KMP(Z 函数) 算法详解

扩展KMP (ex_KMP)的更多相关文章

  1. HDU 6153 扩展kmp

    A Secret Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 256000/256000 K (Java/Others)Total ...

  2. HDU 3613 Best Reward(扩展KMP求前后缀回文串)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3613 题目大意: 大意就是将字符串s分成两部分子串,若子串是回文串则需计算价值,否则价值为0,求分割 ...

  3. 扩展KMP算法

    一 问题定义 给定母串S和子串T,定义n为母串S的长度,m为子串T的长度,suffix[i]为第i个字符开始的母串S的后缀子串,extend[i]为suffix[i]与字串T的最长公共前缀长度.求出所 ...

  4. 扩展KMP --- HDU 3613 Best Reward

    Best Reward Problem's Link:   http://acm.hdu.edu.cn/showproblem.php?pid=3613 Mean: 给你一个字符串,每个字符都有一个权 ...

  5. KMP和扩展KMP

    文章网上太多这里提一下代码细节: KMP: scanf("%s\n",s); scanf("%s\n",t); int ls=strlen(s),lt=strl ...

  6. UVA5876 Writings on the Wall 扩展KMP

    扩展KMP的简单题. #include<stdio.h> #include<string.h> #define maxn 51010 char s[maxn],t[maxn]; ...

  7. hdu4333 扩展KMP

    慢慢研究可以发现,可以用扩展kmp来求.由于扩展kmp的next[]只有一部分,当前位子前面那部分和母串的后部分,所以可以将字符串复制接在后面一次. 先求如果next[]>0&& ...

  8. 扩展KMP

    刘雅琼论文 http://wenku.baidu.com/view/8e9ebefb0242a8956bece4b3.html 论文讲的非常详细. 给定母串S,子串T,n=strlen(S),m=st ...

  9. HDU 3336 扩展kmp

    题目大意: 找到字符串中所有和前缀字符串相同的子串的个数 对于这种前缀的问题,通常通过扩展kmp来解决 其实吧这是我第一次做扩展kmp的题目,原来确实看过这个概念,今天突然做到,所以这个扩展kmp的模 ...

  10. acdream1116 Gao the string!(扩展KMP)

    今天是字符串填坑的一天,首先填的第一个坑是扩展KMP.总结一下KMP和扩展KMP的区别. 在这里s是主串,t是模式串. KMP可以求出的是以s[i]为结尾的串和 t前缀匹配的最长的长度.假如这个长度是 ...

随机推荐

  1. Js 中的数组

    在js 中,数组就是对象,除了可以使用字面量语法[...]来声明数组外,它和其它对象没有什么区别.当创建一个数组['a', 'b', 'c']时,内部的实现形式如下: {     length: 3, ...

  2. ubuntu 同时安装python2 和 python3 版本的 gunicorn

    前言 最近在学习使用 gunicorn 部署 flask 项目.发现使用 pip3 安装完 gunicorn后,如如果再使用 pip2 安装 gunicorn,后安装的 gunicorn 就会覆盖掉原 ...

  3. VPS测试脚本,网络线路,路由测试,流媒体服务器测试脚本

    ​ 收集了一些服务器测试脚本,测试性能,网络以及解锁Netflix等服务.记录收集一下,特此记录. yabs测试脚本wget -qO- yabs.sh | bash 老外比较爱用的服务器性能测试脚本. ...

  4. oeasy教您玩转vim - 17 - # 向上向下

    向上向下 回忆上节课内容 和 f 相关的是跳到 向 前 跳到是 f 向 后 跳到是 F 和 t 相关的是贴靠 向 前 贴靠是 t 向 后 贴靠是 T 和 小写 相关的是 向前 向前 跳跃是 f 向前 ...

  5. AT_arc113_c 题解

    洛谷链接&Atcoder 链接 本篇题解为此题较简单做法及较少码量,并且码风优良,请放心阅读. 题目简述 现在有一个字符串 \(S\),每一次你可以选择一个 \(i(1 \le i \le | ...

  6. 测试思想-流程规范 用例优先级定义与使用规范 V1.0

    用例优先级定义与使用规范 V1.0 By:授客   1.      规范说明 目的 对软件测试过程中的用例级别进行详细描述及标准化定义,明确不同测试阶段的测试范围,减少测试冗余投入,提高测试效率,建立 ...

  7. 微软CrowdStrike驱动蓝屏以及内核签名

    原因 当Windows操作系统遇到严重错误导致系统崩溃时,屏幕显示为蓝色,通常伴有错误代码和信息,这被称为"蓝屏死机"(Blue Screen of Death,简称BSOD) h ...

  8. 解锁Nginx日志的宝藏:GoAccess——你的实时、交互式Web日志分析神器!

    在当今数字化的时代,网站的流量和用户行为数据就像是一座蕴藏着无尽秘密的宝藏.而如何有效地挖掘和分析这些数据,成为了许多网站管理者和开发者头疼的问题.GoAccess,一款开源的实时Web日志分析工具, ...

  9. JAVA课后谈1

    1.课程中动手动脑的问题 在本次课堂实验中,我暴露出了很多问题,尤其是逻辑思维方面,不能很好的处理数理运算问题.在实际生活中客户的要求千变万化,而我们作为一个合格的程序设计者应该顺应其要求,尽可能的去 ...

  10. 使用 $fetch 进行 HTTP 请求

    title: 使用 $fetch 进行 HTTP 请求 date: 2024/8/2 updated: 2024/8/2 author: cmdragon excerpt: 摘要:文章介绍了Nuxt3 ...