z函数(kmp)

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数组和p数组,首先1+p[i]是z数组对应的那个字符串,i+p[i]是p数组对应的那个数组,而且求p数组的时候要防越界
还有min函数里面第一个数组里面是i-l+1,第二个是r-i+1;
数组的权值计算方式如下:

对于长度为 ( n ) 的数组 ( a ),其权值定义为将每个下标 ( i )(从 ( 1 ) 到 ( n ))对应的值 ( i \times (a_i + 1) ) 进行按位异或(XOR)运算后的结果。具体步骤如下:

  1. 遍历数组:对每个元素 ( a_i )(( i ) 从 ( 1 ) 到 ( n )),计算 ( i \times (a_i + 1) )。
  2. 异或运算:将所有计算结果逐个进行异或操作,最终结果即为数组的权值。

数学表达式
[
\text{权值} = \bigoplus_{i=1}^{n} \left( i \times (a_i + 1) \right)
]
其中,( \bigoplus ) 表示按位异或运算。

示例

  • 当 ( a = [1, 2, 3] ) 且 ( n = 3 ) 时:
    • ( i=1 ): ( 1 \times (1+1) = 2 )
    • ( i=2 ): ( 2 \times (2+1) = 6 )
    • ( i=3 ): ( 3 \times (3+1) = 12 )
    • 权值 = ( 2 \oplus 6 \oplus 12 = 8 )。

关键点

  • 异或运算满足交换律和结合律,计算顺序不影响结果。
  • 下标 ( i ) 从 ( 1 ) 开始,到 ( n ) 结束。
  • 每个项的计算顺序:先计算 ( a_i + 1 ),再乘以 ( i )。
#include<iostream>
#include<string>
using namespace std;
const int N=2*1e7+5;
long long int  p[N];
long long int  z[N];
int main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
    string s1,s2;
    cin>>s1>>s2;
    int n=s1.size();
    int m=s2.size();
    s1=" "+s1;
    s2=" "+s2;
    z[1]=m;
    for(int i=2,l,r=0;i<=m;i++){
        if(i<=r)z[i]=min(z[i-l+1],r-i+1ll);
        while(s2[1+z[i]]==s2[i+z[i]])z[i]++;
        if(i+z[i]-1>r)l=i,r=i+z[i]-1;
    }
    for(int i=1,l,r=0;i<=n;i++){
        if(i<=r)p[i]=min(z[i-l+1],r-i+1ll);
        while(1+p[i]<=m&&i+p[i]<=n&&s2[1+p[i]]==s1[i+p[i]])p[i]++;
        if(i+p[i]-1>r)l=i,r=i+p[i]-1;
    }
    long long int ans1=0,ans2=0;
    for(int i=1;i<=m;i++)z[i]=i*(z[i]+1),ans1^=z[i];
    for(int i=1;i<=n;i++)p[i]=i*(p[i]+1),ans2^=p[i];
    cout<<ans1<<'\n'<<ans2;
    return 0;
}
posted @ 2025-04-04 21:28  郭轩均  阅读(25)  评论(0)    收藏  举报