P5410 【模板】扩展 KMP / exKMP(Z 函数)
本文最后更新于35 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

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

题目描述

给定两个字符串 $a,b$,你要求出两个数组:

  • $b$ 的 $z$ 函数数组 $z$,即 $b$ 与 $b$ 的每一个后缀的 LCP 长度。
  • $b$ 与 $a$ 的每一个后缀的 LCP 长度数组 $p$。

对于一个长度为 $n$ 的数组 $a$,设其权值为 $\operatorname{xor}_{i=1}^n i \times (a_i + 1)$。

输入格式

两行两个字符串 $a,b$。

输出格式

第一行一个整数,表示 $z$ 的权值。

第二行一个整数,表示 $p$ 的权值。

输入输出样例 #1

输入 #1

aaaabaa
aaaaa

输出 #1

6
21

说明/提示

样例解释:

$z = {5\ 4\ 3\ 2\ 1}$,$p = {4\ 3\ 2\ 1\ 0\ 2\ 1}$。


数据范围:

对于第一个测试点,$|a|,|b| \le 2 \times 10^3$。

对于第二个测试点,$|a|,|b| \le 2 \times 10^5$。

对于 $100\%$ 的数据,$1 \le |a|,|b| \le 2 \times 10^7$,所有字符均为小写字母。

思路

Z 函数用于求原字符串与每个后缀的最大匹配长度。$z[1]=n$。遍历位置 i 时,如果 i 位于之前的匹配盒子 $[l,r]$ 内,就从对应位置 $z[i−l+1]$ 继承,但最多继承 $r−i+1$ 个字符;然后继续暴力扩展,并更新最靠右的匹配盒子。

GPT Said

z函数用于求解原字符串和它的每个后缀的最长公共前缀长度。
$z[1]$是完全匹配的,所以默认就是字符串长度。
然后开始遍历,计算过程中维护$[l,r]$,满足$s[l…r]$与字符串前缀相同。遍历到位置i时,如果$i<=r$,说明$i$在已知匹配区间内,可以先令 $z[i]=min(z[i-l+1],r-i+1)$ 继承已经确定的匹配长度;如果$i>r$,则$z[i]=0$开始。随后继续逐字符暴力比较,扩展$z[i]$。如果当前匹配区间$[i,i+z[i]-1]$的右端点超过了$r$,就更新
$l=i$ , $r=i+z[i]-1$

for (int i = 2; i <= n; i++) {
    // 1. 继承
    if (i <= r) {
        z[i] = min(z[i - l + 1], r - i + 1);
    }

    // 2. 暴力扩展
    while (i + z[i] <= n &&
           s[i + z[i]] == s[1 + z[i]]) {
        z[i]++;
    }

    // 3. 更新盒子
    if (i + z[i] - 1 > r) {
        l = i;
        r = i + z[i] - 1;
    }
}

最简记忆:

i 在盒子内 → 先继承
继承不能越过 r → 取 min
然后继续比较 → 暴力扩展
扩展得更远 → 更新 [l,r]

代码

//Sunshine sunshine ladybugs awake,
//Clap your hooves and do a little shack.
#include<bits/stdc++.h>
#define int long long
#define endl "\n"

using namespace std;

using PII=pair<int,int> ;
const int MAXN=300005;
const int mod=998244353;
const int INF=0x3f3f3f3f3f3f3f3f;

void solve(){
    string a,b;
    cin>>a>>b;

    a=" "+a;
    b=" "+b;

    int n=(int)a.size()-1;
    int m=(int)b.size()-1;

    vector<int> z(m+1);
    z[1]=m;

    int l=1,r=0;

    for(int i=2;i<=m;i++){
        if(i<=r){
            int k=i-l+1;
            int remain=r-i+1;

            z[i]=min(z[k],remain);
        }

        while(i+z[i]<=m&&
              b[1+z[i]]==b[i+z[i]]){
            z[i]++;
        }

        if(z[i]>0&&i+z[i]-1>r){
            l=i;
            r=i+z[i]-1;
        }
    }

    int answerZ=0;
    for(int i=1;i<=m;i++){
        answerZ^=1ll*i*(z[i]+1);
    }



    int answerP=0;
    l=1;r=0;
    for(int i=1;i<=n;i++){
        int now=0;
        if(i<=r){
            int k=i-l+1;
            int remain=r-i+1;
            now=min(z[k],remain);
        }

        while(now<m&&
              i+now<=n&&
              a[i+now]==b[1+now]){
            now++;
        }

        if(now>0&&i+now-1>r){
            l=i;
            r=i+now-1;
        }

        answerP^=1ll*i*(now+1);
    }

    cout<<answerZ<<endl;
    cout<<answerP<<endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    //int t;cin>>t;while(t--)
    solve();
    return 0;
}

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇