• 算法竞赛进阶指南——后缀数组


    后缀数组

    后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围。
    在本题中,我们希望使用快排、Hash与二分实现一个简单的O(nlog2n)的后缀数组求法。
    详细地说,给定一个长度为 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


    说实话看到这道题的时候真的是一脸懵,这东西咋用hash,二分,快排。
    后来找了些博客看,终于理解其中的思路了,
    一、先记录整条的hash值。
    二、用一个sort函数,自定义cmp。
    三、通过二分确定两个后缀字符之间的前缀相同字母。

    这道题写这篇题解的时候又重新去写了一遍,感觉真的难。

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    typedef unsigned long long ull;
    const int base = 131;
    const int N = 3e5 + 10;
    char str[N];
    ull h[N], p[N];
    int sa[N], n;
    ull gethash(int l, int r) {//得到某一段的hash值
        return h[r] - h[l - 1] * p[r - l + 1];
    }
    int sumsub(int a, int b) {
        int l = 0, r = min(n - a + 1, n - b + 1);//取最小的。
        while(l < r) {//二分。
            int mid = (l + r + 1) >> 1;
            if(gethash(a, a + mid - 1) != gethash(b, b + mid - 1))  r = mid - 1;
            else l = mid;
        }
        return r;
    }
    bool cmp(int a, int b) {
        int l = sumsub(a, b);//两个的相同前缀长度。
        int x = a + l > n ? - 1e9 : str[a + l];如果有一个单词都是前缀,防止发生数组越界。
        int y = b + l > n ? - 1e9 : str[b + l];
        return x < y;
    }
    int main() {
        scanf("%s", str + 1);//从第一个字符开始可以避免hash的边界问题。
        n = strlen(str + 1);
        p[0] = 1;
        for(int i = 1; i <= n; i++) {
            h[i] = h[i - 1] * base + str[i] - 'a' + 1;
            p[i] = p[i - 1] * base;
            sa[i] = i;
        }
        sort(sa + 1, sa + n + 1, cmp);//对下标进行排序。
        for(int i = 1; i <= n; i++) printf("%d%c", sa[i] - 1, i == n ? '
    ' : ' ');
        printf("0 ");
        for(int i = 2; i <= n; i++)    printf("%d%c", sumsub(sa[i], sa[i - 1]), i == n ? '
    ' : ' ');
        return 0;
    }
    

    这个算法耗时还是非常长的,并不是真正的能用的算法,但是这个写法的综合力度还是比较高的,思想还是可以借鉴的。

  • 相关阅读:
    微软VS2008月底推出beta 2中文版 搭配.NET 3.5
    Asp.Net AjaxPasswordstrength控件使用
    Asp.Net AjaxHoverMenu控件使用
    Asp.Net Ajax AutoComplete控件使用
    ASP.NET中基类页的设计和使用
    Asp.Net中页面间传值方法
    基于ASP.NET AJAX技术开发在线RSS阅读器(上篇)
    Asp.Net AjaxFilteredTextBox控件使用
    基于ASP.NET AJAX技术开发在线RSS阅读器(下篇)
    Asp.Net AjaxTextBoxWateramrk控件使用
  • 原文地址:https://www.cnblogs.com/lifehappy/p/12601174.html
Copyright © 2020-2023  润新知