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


    题面

    洛谷P5410 【模板】扩展 KMP(Z 函数)

    给定两个字符串 (a,b),要求出两个数组:(b)(z) 函数数组 (z)(b)(a) 的每一个后缀的 LCP 长度数组 (p)

    数据范围:(1le |a|,|b|le 2 imes 10^7)


    蒟蒻语

    别的题解为什么代码那么长、讲解那么复杂?蒟蒻不解,写篇易懂一点的,希望没有错误理解。

    注意:蒟蒻的下标是从 (0) 开始的。


    蒟蒻解

    定义 (z(i) (i>0)):后缀 (i) 与字符串的 LCP 长度,劝退一点地说:

    [z(i)=max_{len=1}^{|s|-i} left(lencdot [forall 0le x<len,s[(0)+(x)]=s[(i)+(x)]] ight) ]

    对于求字符串 (s)(z) 函数,可以用递推解决一部分问题,蒟蒻先放上精美的代码:

    这里特定 (z(0)=0)(题目中 (z(0)=|s|))。

    void getz(string s){
        int l=0;
        R(i,1,sz(s)){
            if(l+z[l]>i) z[i]=min(z[i-l],l+z[l]-i);
            while(i+z[i]<sz(s)&&s[z[i]]==s[i+z[i]]) z[i]++;
            if(i+z[i]>l+z[l]) l=i;
        }
        // R(i,0,sz(s)) cout<<z[i]<<" ";cout<<'
    ';
    }
    

    结论: 对于 (i>0),对任意 (0le l<i) 都可以递推得:

    [forall 0le x<min(z(i-l),l+z(l)-i),s[(i)+(x)]=s[(0)+(x)] ]

    证明:

    [egin{aligned} &s[(i)+(x)]\ =&s[(l)+(i+x-l)]\ =&s[(0)+(i+x-l)]color{red}{(xle l+z(l)-i)}\ =&s[(i-l)+(x)]\ =&s[(0)+(x)]color{red}{(xle z(i-l))}\ end{aligned} ]

    所以可以选定某个 (0le l<i),初始化 (z(i)=min(z(i-l),l+z(l)-i)),然后暴力判断字符相等增加 (z(i))

    这里 (l) 选满足 (j+z(j)(0le j<i)) 最大的 (j),这样每个字符只会被暴力判断一次,所以时间复杂度可以做到 (Theta(n))

    对于题目中的问题其实把 (b)(a) 接起来做个 (z) 就可以了。


    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    //Start
    typedef long long ll;
    typedef double db;
    #define mp(a,b) make_pair((a),(b))
    #define x first
    #define y second
    #define Be begin()
    #define En end()
    #define sz(a) int((a).size())
    #define pb(a) push_back(a)
    #define R(i,a,b) for(int i=(a),I=(b);i<I;i++)
    #define L(i,a,b) for(int i=(b)-1,I=(a)-1;i>I;i--)
    const int iinf=0x3f3f3f3f;
    const ll linf=0x3f3f3f3f3f3f3f3f;
    
    //Data
    const int N=2e7;
    ll ansz,ansp;
    string a,b;
    
    //Zfunction
    int z[N<<1];
    void getz(string s){
        int l=0;
        R(i,1,sz(s)){
            if(l+z[l]>i) z[i]=min(z[i-l],l+z[l]-i);
            while(i+z[i]<sz(s)&&s[z[i]]==s[i+z[i]]) z[i]++;
            if(i+z[i]>l+z[l]) l=i;
        }
        // R(i,0,sz(s)) cout<<z[i]<<" ";cout<<'
    ';
    }
    
    //Main
    int main(){
        ios::sync_with_stdio(0);
        cin.tie(0),cout.tie(0);
        cin>>a>>b,getz(b+a);
        ansz^=1ll*(sz(b)+1)*(0+1);
        R(i,1,sz(b)) ansz^=1ll*(min(z[i],sz(b)-i)+1)*(i+1);
        R(i,0,sz(a)) ansp^=1ll*(min(z[i+sz(b)],sz(b))+1)*(i+1);
        cout<<ansz<<'
    '<<ansp<<'
    ';
        return 0;
    }
    

    祝大家学习愉快!

  • 相关阅读:
    Forethought Future Cup
    2020 CSP-S T3 函数调用(拓扑序+后缀积)
    欧拉图论定理 公式及证明
    2020 CSP-J T3 表达式(栈+二叉树)
    eJOI2018 Problem D Chemical table(二分图+并查集)
    洛谷 P5089 [eJOI2018]元素周期表(二分图+并查集)
    eJOI 2017 Problem A Magic(前缀和+排序)
    洛谷 P6273 [eJOI2017]魔法(前缀和+排序)
    OI常见解题思路技巧大汇总【不定期更新】
    JZOJ 6840. 【2020.11.5提高组模拟】铲雪(线段树)
  • 原文地址:https://www.cnblogs.com/George1123/p/13766407.html
Copyright © 2020-2023  润新知