算法竞赛进阶指南---0x14(Hash)后缀数组

题面

在这里插入图片描述

题解

  1. 我们先来看朴素做法,对于一个长度为n的字符串,它的后缀也有n个,将他们排序(用快排)是 O(nlogn) ,如果是暴力比较两个字符串的字典序是 O(n) ,那么总的时间复杂度就是O(n2logn) ,显然不符合题中要求,那么如何做到题中所说的O(nlog2n) 呢?按题中思路来
  1. 我们的后缀数组用字符串下标表示 sa[i] 就表示下标 i到n的字符 例如sa[1]表示"ponoiiipoi" sa[10]表示"i"
    这样我们虽然在快排的时候无法优化,但是可以在比较两个字符串的时候可以优化,对于两个后缀字符串字串,例如 a=“ipoi” , b=“iiipoi” 我们先用二分来找出这两个字串的最大公共前缀,然后再比较公共前缀的后一位即可判断出哪个的字典序大 ,因为二分是O(logn),比较是O(1) ,所以可以优化为O(nlog2n)
  1. 二分的check函数怎么写,就是如何判断二分的值是不是最长公共前缀,因为我们的后缀串都是原串的字串,我们预先求出原串的hash值,然后就可以求出任意区间的hash值,那么对于两端区间,只要是hash值相等,那么我们就认为这两个字串相等,我们就可以用hash值是否相等来作为二分的条件

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<limits.h>

using namespace std;
typedef unsigned long long ULL;
const int N = 3e5 + 10;
const int P = 131;

int n;
char s[N];
ULL h[N], p[N];
int sa[N];

//计算字串的hash值
ULL get(int l, int r) {
    
    
    return h[r] - h[l - 1] * p[r - l + 1];
}

//二分找两个后缀串的最长公共前缀
int get_max_common_prefix(int a, int b) {
    
    
    int l = 0;
    int r = min(n - a + 1, n - b + 1);
    while (l < r) {
    
    
        int mid = (l + r + 1) >> 1;
        if (get(a, a + mid - 1) != get(b, b + mid - 1)) {
    
    
            r = mid - 1;
        } else {
    
    
            l = mid;
        }
    }
    return l;
}

//比较两个后缀串的字典序
bool cmp(int a, int b) {
    
    
    int len = get_max_common_prefix(a, b);
    int va = a + len > n ? INT_MIN : s[a + len];
    int vb = b + len > n ? INT_MIN : s[b + len];
    return va < vb;
}

int main() {
    
    

    scanf("%s", s + 1);
    n = strlen(s + 1);
    p[0] = 1;
    for (int i = 1; i <= n; i++) {
    
    
        h[i] = h[i - 1] * P + s[i];
        p[i] = p[i - 1] * P;
        sa[i] = i;
    }
    sort(sa + 1, sa + 1 + n, cmp);

    for (int i = 1; i <= n; i++) printf("%d ", sa[i] - 1);
    cout << endl;

    for (int i = 1; i <= n; i++) {
    
    
        if (i == 1) {
    
    
            printf("%d ", 0);
        } else {
    
    
            printf("%d ", get_max_common_prefix(sa[i - 1], sa[i]));
        }
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44791484/article/details/113818735
今日推荐