Z函数(扩展kmp)原理简介

作用

  • 字符串 S [ 0... n − 1 ] S[0...n-1] S[0...n1] Z [ i ] Z[i] Z[i]数组, Z [ i ] Z[i] Z[i]表示 S [ 0... n − 1 ] S[0...n-1] S[0...n1] S [ i . . . n − 1 ] S[i...n-1] S[i...n1]的最长公共前缀 ( L C P ) (LCP) (LCP)

原理

运用自动机的思想寻找限制条件下的状态转移函数,使得可以借助之前的状态加速计算新的状态
—摘自oiwiki

  • 类似 k m p , m a n a c h e r kmp,manacher kmp,manacher等字符串算法,我们从前往后递推求解,不重复计算,利用之前计算过的来得到新的值,使得将常规做法 O ( n 2 ) O(n^2) O(n2)的时间复杂度优化到 O ( n ) O(n) O(n)

约定字符串从 0 0 0开始,我们维护一个 [ l , r ] [l,r] [l,r]的区间,称为 Z − b o x Z-box Zbox,这个区间表示当前计算到的右端点最远的 L C P LCP LCP,此区间内部的 Z Z Z值我们几乎都可以 O ( 1 ) O(1) O(1)得到,因为根据 Z − b o x Z-box Zbox定义,如果 i i i在这个范围内且 L C P LCP LCP长度合理 ( Z [ i − l ] ≤ r − i + 1 ) (Z[i-l]\leq r-i+1) (Z[il]ri+1),有 Z [ i ] = Z [ i − l ] Z[i]=Z[i-l] Z[i]=Z[il],如果长度不合理,则更新为 r − i + 1 r-i+1 ri+1,之后暴力拓展;然后更新 Z − b o x Z-box Zbox即可,代码如下

void get_z(string &s){
  int len = s.length();
  Z[0] = len;
  for(int i=1,l=0,r=0;i<len;i++){
    if(i <= r) Z[i] = min(Z[i - l], r - i + 1);
    while(s[Z[i]] == s[i + Z[i]]) Z[i] += 1;
    if(i + Z[i] - 1 > r){
      l = i;
      r = i + Z[i] - 1;
    }
  }
}

例题

https://www.luogu.com.cn/problem/P5410

  • 求字符串 b b b Z Z Z数组和 b b b和字符串 a a a的每一个后缀的 L C P LCP LCP
  • 我们做一个字符串 c = b + a c=b+a c=b+a,然后计算字符串 c c c Z Z Z数组,然后根据性质易得
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 4e7 + 100;

int Z[N];
string s;
int get_Z(int len){
  Z[0] = len;
  for(int i=1,l=0,r=0;i<len;i++){
    if(i <= r) Z[i] = min(Z[i - l], r - i + 1);
    while(s[Z[i]] == s[i + Z[i]]) Z[i] += 1;
    if(i + Z[i] - 1 > r){
      l = i;
      r = i + Z[i] - 1;
    }
  }
};
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  string a, b;
  cin >> a >> b;
  int a_len = a.length();
  int b_len = b.length();
  s = b + a;
  get_Z((int)s.length());
  ll ans = 0;
  for(int i=0;i<b_len;i++){
    ans ^= 1ll * (i + 1) * (1 + min(1ll * Z[i], 1ll * b_len - i));
  }
  cout << ans << '\n';
  ans = 0;
  for(int i=0;i<a_len;i++){
    ans ^= 1ll * (i + 1) * (1 + min(1ll * Z[i + b_len], 1ll * a_len));
  }
  cout << ans;
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clarence Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值