• CH 1402


    题目链接:传送门

    描述

    后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围。

    在本题中,我们希望使用快排、Hash与二分实现一个简单的 $O(n log^2 ⁡n )$ 的后缀数组求法。

    详细地说,给定一个长度为 n 的字符串S(下标 0~n-1),我们可以用整数 k(0≤k<n) 表示字符串S的后缀 S(k~n-1)。

    把字符串S的所有后缀按照字典序排列,排名为 i 的后缀记为 SA[i]。额外地,我们考虑排名为 i 的后缀与排名为 i-1 的后缀,把二者的最长公共前缀的长度记为 Height[i]。

    我们的任务就是求出SA与Height这两个数组。

    输入格式

    一个字符串,长度不超过30万。

    输出格式

    第一行为数组SA,相邻两个整数用1个空格隔开。

    第二行为数组Height,相邻两个整数用1个空格隔开,特别地,假设Height[1]=0。

    样例输入

    ponoiiipoi

    样例输出

    9 4 5 6 2 8 3 1 7 0
    0 1 2 1 0 0 2 1 0 2

    样例解释

    排名第一(最小)的后缀是9(S[9~9],即字符串 i),第二的是后缀4(S[4~9],即字符串iiipoi),第三的是后缀5(S[5~9],即字符串iipoi)以此类推。Height[2]表示排名第2与第1的后缀的最长公共前缀,长度为1,Height[3]表示排名第3与第2的后缀的最长公共前缀,长度为2,以此类推。

    题解:

    假设字符串长度为 $N$,那么如果用暴力的方式来比较两个后缀子串的字典序大小(以及求最长公共前缀的长度),显然一次比较是 $Oleft( {N} ight)$ 的时间复杂度,

    如果用快排对 $N$ 个后缀子串进行排序,那么就要 $O(N^2 log N)$ 的时间复杂度,

    我们尝试考虑优化的地方:比较两个字符串的时间,从 $O(N)$ 降到 $O(log N)$,

    对于两个后缀子串,或者更一般的,对于两个字符串,怎么更快速的比较字典序大小,或者,怎么更快速求的最长公共前缀?

    容易想到,可以二分最长公共前缀的长度,用字符串哈希 $O(1)$ 判断是否两个前缀子串是否一样,

    然后判断两个字符串中哪个字典序更大就很简单了,去掉最长公共前缀,比较一下剩下的第一个字符即可。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ull;
    
    const int P=131;
    const int maxn=300000+10;
    
    char s[maxn];
    int len;
    int sa[maxn],h[maxn];
    
    ull pre[maxn],Ppow[maxn];
    void pretreat()
    {
        pre[0]=0;
        Ppow[0]=1;
        for(int i=1;i<=len;i++)
        {
            pre[i]=pre[i-1]*P+(s[i]-'a'+1);
            Ppow[i]=Ppow[i-1]*P;
        }
    }
    
    inline bool isSame(int l1,int r1,int l2,int r2)
    {
        return pre[r1]-pre[l1-1]*Ppow[r1-(l1-1)] == pre[r2]-pre[l2-1]*Ppow[r2-(l2-1)];
    }
    inline int maxpre(int a,int b)
    {
        int l=0,r=min(len-a+1,len-b+1),mid;
        while(l<r)
        {
            mid=(l+r)/2+1;
            if(isSame(a,a+mid-1,b,b+mid-1)) l=mid;
            else r=mid-1;
        }
        return l;
    }
    bool cmp(int a,int b)
    {
        int mp=maxpre(a,b);
        return s[a+mp]<=s[b+mp];
    }
    
    int main()
    {
        scanf("%s",s+1);
        len=strlen(s+1);
        pretreat();
        for(int i=1;i<=len;i++) sa[i]=i;
        sort(sa+1,sa+len+1,cmp);
        for(int i=1;i<=len;i++)
        {
            if(i==1) h[i]=0;
            else h[i]=maxpre(sa[i-1],sa[i]);
            printf("%d%c",sa[i]-1,i<len?' ':'
    ');
        }
        for(int i=1;i<=len;i++) printf("%d%c",h[i],i<len?' ':'
    ');
    }

    1A开心

    时间复杂度:

    排序 $O(N log^2 N)$,计算Height数组 $O(N log N)$,总时间复杂度 $O(N log^2 N)$。

  • 相关阅读:
    vb笔记
    linux学习笔记
    linnux--shell
    # 用类来封装动态数组:分文件编写
    面向对象
    c++2
    c++1
    答疑:指针数组字符串
    文件操作
    用函数封装实现对一个数组增删改查
  • 原文地址:https://www.cnblogs.com/dilthey/p/9695162.html
Copyright © 2020-2023  润新知