zoukankan      html  css  js  c++  java
  • 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)$。

  • 相关阅读:
    【LeetCode】048. Rotate Image
    【LeetCode】036. Valid Sudoku
    【LeetCode】060. Permutation Sequence
    【LeetCode】001. Two Sum
    【LeetCode】128. Longest Consecutive Sequence
    【LeetCode】081. Search in Rotated Sorted Array II
    【LeetCode】033. Search in Rotated Sorted Array
    顺时针打印矩阵
    矩形覆盖
    二维数组中的查找
  • 原文地址:https://www.cnblogs.com/dilthey/p/9695162.html
Copyright © 2011-2022 走看看