exkmp 用于求解这样的问题:

求文本串 \(T\) 的每一个后缀与模式串 \(M\) 的匹配长度(即最长公共前缀长度)。特别的,取 \(M=T\),得到的这个长度被称为 \(Z\) 函数。“函数”只是一个叫法,它本质上是个数组...为了好听,后面叫他“\(Z\) 数组” (互联网上的确有人这么叫)

符号(字符串)

\(|S|\) 表示 \(S\) 的长度

\(S[l:r]\) 表示 \(S\) 从 \(l\) 到 \(r\) 的子串。如果 \(l\) 空着,默认为 \(1\);同理 \(r\) 默认为 \(|S|\)。

也就是 \(S[:x]\) 表示 \(S\) 到 \(x\) 结束的前缀,\(S[x:]\) 表示 \(S\) 从 \(x\) 开始的后缀。

\(LCP(S_1,S_2)\) 表示 \(S_1,S_2\) 的 最长公共前缀Longest Common Prefix

算法讲解

设 \(p_i=LCP(T_i,M)\)

定义从 \(l\) 开始的匹配区间为 \([l,l+p_l-1]\) (设 \(l+p_l-1=r\))

我们枚举处理。假设现在已经求好了 \([1,i-1]\) 的 \(p\) 数组,要求 \(p_i\)。记录一个 最靠后 的匹配区间 \([l,r]\) (\(l<i\),以 \(r\) 靠后为第一关键字,\(l\) 靠后为第二关键字),考虑直接从 \([l,r]\) 中继承点答案来,那很显然一个前提就是 \(i\le r\) (你 \(i\) 在 \(r\) 外面继承啥)

显然,\(p_i\ge LCP(T[i:r],M)\) (因为 \(T[i:r]\) 是 \(T[i:]\) 前缀)

由定义, \([l,r]\) 是最长匹配长度,可知 \(T[l:r]=M[1:r-l+1]\)。

然后现在假如 \(l<i\le r\),那么显然 \(T[i:r]=M[i-l+1:r-l+1]\)

那么 \(LCP(T[i:r],M)=LCP(M[i-l+1:r-l+1],M)\)

简单想一下,\(LCP(A[l:r],A)=min(LCP(A[l:],A),r-l+1)\)

我们要求 \([l,r]\) 子串与整个串的 \(LCP\),可以先求以 \(l\) 开头的整个后缀的与整个串的 \(LCP\),然后和区间长度取 \(min\)。这显然正确。

然后有:

\(LCP(M[i-l+1:r-l+1],M)=min(LCP(M[i-l+1:],M),(r-l+1)-(i-l+1+1))\)

右边的 \(-l+1\) 两个抵消了,就变成 \(r-i+1\)

然后前面是 \(LCP(M[i-l+1:],M)\) 。这不就是 \(M\) 的 \(Z\) 数组的第 \(i-l+1\) 个位置吗!(还记得 \(Z\) 数组的定义吗?)

觉得看字母理解不了的看图(自己画的)(纯鼠标):

红色的部分就是我们推出来的匹配部分。然后现在我们把 \(M\) 移到 \(i\) 开头的位置来匹配,就相当于把 \(M[i-l+1:r-l+1]\) 这一段(红色)移到 \(M\) 的开头处匹配。这一段匹配的长度就是 \(min(Z_{i-l+1},r-i+1)\)。

假设我们现在能求这个 \(Z\) 数组,那么我们已经知道 \(p_i\) 的最小值了 ,就是 \(min(Z_{i-l+1},r-i+1)\) 。从这个位置开始暴力即可。这样就不用每次从 \(1\) 开始匹配了。

求完 \(p_i\) 之后,记得用 \([i,i+p_i-1]\) 更新 \([l,r]\)。

update 2021.01.20:

时间是线性的。原因是,每次成功匹配之后,能直接继承答案的区间便多了一位,复杂度便是线性的。

证明由神仙学长 tzxydby 提供

如何求 Z 数组

我们发现 \(Z\) 数组就是自己和自己匹配的过程。然后我们把上面过程中 \(M\) 换成 \(T\) 即可。

所以我们还是记录一个最靠后的匹配区间 \([l,r]\),然后 \(p_i\) 就相当于 \(Z_i\) 了。

易得:

\(Z_i=min(LCP(M[i-l+1:],M),r-i+1)=min(LCP(T[i-l+1:],T),r-i+1)=min(Z_{i-l+1},r-i+1)\)

求完 \(Z_i\) 之后,记得用 \([i,i+Z_i-1]\) 来更新 \([l,r]\)。

一样,也是从这里开始暴力即可。同理,时间复杂度依然是线性的。

模板

洛谷板子

#include <bits/stdc++.h>
using namespace std;
#define N 20000007
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
#define Tra(i,u) for(int i=G.Start(u),v=G.To(i);~i;i=G.Next(i),v=G.To(i))
#define p_b push_back
#define sz(a) ((int)a.size())
#define all(a) a.begin(),a.end()
#define iter(a,p) (a.begin()+p)
#define Flandre_Scarlet int
#define IsMyWife main
char _c;
int I()
{
int x=0; int f=1;
while(_c<'0' or _c>'9') f=(_c=='-')?-1:1,_c=getchar();
while(_c>='0' and _c<='9') x=(x<<1)+(x<<3)+(_c^48),_c=getchar();
return (x=(f==1)?x:-x);
}
void Rd(int cnt,...)
{
va_list args; va_start(args,cnt);
F(i,1,cnt) {int* x=va_arg(args,int*);(*x)=I();}
va_end(args);
} char a[N],b[N];
void Input()
{
scanf("%s%s",a+1,b+1);
}
int z[N];
void Z(char s[]) // 求 Z 函数
{
int n=strlen(s+1);
z[1]=n; F(i,2,n) z[i]=0;
// Z[1]=n 特判,同时也是递推边界
int l=0,r=0;
F(i,2,n)
{
if (i<=r) z[i]=min(z[i-l+1],r-i+1); // 推理出下界
while(i+z[i]<=n and s[i+z[i]]==s[z[i]+1]) ++z[i]; // 暴力
if (i+z[i]-1>=r) l=i,r=i+z[i]-1; // 更新最靠后的匹配位置
}
}
int p[N];
void ExKmp(char s[],char t[])
{
int n=strlen(s+1);
Z(t);
int l=0,r=0;
F(i,1,n)
{
if (i<=r) p[i]=min(z[i-l+1],r-i+1); // 推理出下界
while(i+p[i]<=n and s[i+p[i]]==t[p[i]+1]) ++p[i]; // 暴力
if (i+p[i]-1>r) l=i,r=i+p[i]-1; // 更新最靠后的匹配位置
}
}
void Soviet()
{
ExKmp(a,b);
int n=strlen(a+1),m=strlen(b+1);
long long ans=0;
F(i,1,m) ans^=1ll*i*(z[i]+1);
printf("%lld\n",ans);
ans=0;
F(i,1,n) ans^=1ll*i*(p[i]+1);
printf("%lld\n",ans);
}
Flandre_Scarlet IsMyWife()
{
Input();
Soviet();
getchar();
return 0;
}

exkmp(Z函数) 笔记的更多相关文章

  1. luogu P5410 模板 扩展 KMP Z函数 模板

    LINK:P5410 模板 扩展 KMP Z 函数 画了10min学习了一下. 不算很难 思想就是利用前面的最长匹配来更新后面的东西. 复杂度是线性的 如果不要求线性可能直接上SA更舒服一点? 不管了 ...

  2. MySQL函数笔记

    MySQL函数笔记 日期函数 SELECT t1.xcjyrq, t1.* FROM view_sbxx t1 WHERE t1.syzt ; SELECT t1.xcjyrq, t1.* FROM ...

  3. JavaScript基础——JavaScript函数(笔记)

    avaScript 函数(笔记) JavaScript 是函数式编程语言,在JavaScript脚本中可以随处看到函数,函数构成了JavaScript源代码的主体. 一.定义函数 定义函数的方法有两种 ...

  4. STL之vector常用函数笔记

    STL之vector常用函数笔记 学会一些常用的vector就足够去刷acm的题了 ps:for(auto x:b) cout<<x<<" ";是基于范围的 ...

  5. numpy函数笔记(持续更新)

    numpy函数笔记 np.isin用法 np.isin(a,b) 用于判定a中的元素在b中是否出现过,如果出现过返回True,否则返回False,最终结果为一个形状和a一模一样的数组.(注意:这里的a ...

  6. 题解-洛谷P5410 【模板】扩展 KMP(Z 函数)

    题面 洛谷P5410 [模板]扩展 KMP(Z 函数) 给定两个字符串 \(a,b\),要求出两个数组:\(b\) 的 \(z\) 函数数组 \(z\).\(b\) 与 \(a\) 的每一个后缀的 L ...

  7. Atcoder Regular Contest 058 D - 文字列大好きいろはちゃん / Iroha Loves Strings(单调栈+Z 函数)

    洛谷题面传送门 & Atcoder 题面传送门 神仙题. mol 一发现场(bushi)独立切掉此题的 ycx %%%%%%% 首先咱们可以想到一个非常 naive 的 DP,\(dp_{i, ...

  8. 前缀函数与Z函数介绍

    字符串算法果然玄学=_= 参考资料: OI Wiki:前缀函数与KMP算法 OI Wiki:Z函数(扩展KMP) 0. 约定 字符串的下标从 \(0\) 开始.\(|s|\) 表示字符串 \(s\) ...

  9. KMP&Z函数详解

    KMP 一些简单的定义: 真前缀:不是整个字符串的前缀 真后缀:不是整个字符串的后缀 当然不可能这么简单的,来个重要的定义 前缀函数: 给定一个长度为\(n\)的字符串\(s\),其 \(前缀函数\) ...

随机推荐

  1. Oracle中除数为0的两种解决办法(decode与nullif)

    Oracle中Decode函数,语句DECODE(tag,''ZCGS'',0,1)=decode(''@corp-No@'',''6010'',1,0) decode(字段或字段的运算,值1,值2, ...

  2. Qt学习笔记-更高级的文本编辑器-完善第一版-gif动画

    现在的浏览器大多都有动作图标,现在我们也为浏览器加上图标. 在网上搜索到了几个ico的图标.现在直接拿来用. 首先创建资源文件. 在工程名上鼠标右键,选择add new 然后再选择 Qt Resour ...

  3. python-scrapy爬取某招聘网站(二)

    首先要准备python3+scrapy+pycharm 一.首先让我们了解一下网站 拉勾网https://www.lagou.com/ 和Boss直聘类似的网址设计方式,与智联招聘不同,它采用普通的页 ...

  4. springboot 发布 war jar区别

    fatjar 看下springboot打成jar包后的结构和内容: springboot项目打包的jar 普通jar: 传统jar 通过上面两个图的对比,我们知道这个JAR包与传统JAR包的不同之处在 ...

  5. 一次MySQL死锁的排查记录

    前几天线上收到一条告警邮件,生产环境MySQL操作发生了死锁,邮件告警的提炼出来的SQL大致如下. update pe_order_product_info_test set end_time = ' ...

  6. 自动化运维工具-Ansible之6-Jinja2模板

    自动化运维工具-Ansible之6-Jinja2模板 目录 自动化运维工具-Ansible之6-Jinja2模板 Ansible Jinja2模板概述 Ansible Jinja2模板使用 Ansib ...

  7. 达梦数据库学习(一、linux操作系统安装及数据库安装)

    达梦数据库学习(一.linux操作系统安装及数据库安装) 环境介绍: 使用VM12+中标麒麟V7.0操作系统+达梦8数据库 一.linux系统搭建 本部分没有需要着重介绍,注意安装时基本环境选择&qu ...

  8. ceph对接k8s storage class

    简介 对接ceph的rbd和cephfs到k8s中提供持久化存储 环境 主机名 IP role 操作系统 ceph-01 172.16.31.11 mon osd CentOS7.8 ceph-02 ...

  9. AttGAN: Facial Attribute Editing by Only Changing What You Want 论文阅读笔记和AttGan的pytorch代码实现

    1.总体框架 上面的过程用详细描述即是 Test阶段: Train阶段: 由于我们无法得知编辑后的image,所以显而易见人脸属性编辑是一个无监督问题,而对于我们的xa需要获得关于b的属性,故利用at ...

  10. Azure Key Valut 简介

    Azure Key Vault(密钥库)是用于安全地存储和访问Secret的云服务,Secret是需要严格控制访问权限的内容,例如API密钥,密码,证书或加密密钥.Key Vault Service支 ...