ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

虚树,KD-Tree,长链剖分,后缀数组,后缀自动机

2022-06-06 08:02:26  阅读:86  来源: 互联网

标签:长链 剖分 Lcp 后缀 height int sa include


真的就是讲课两天,吸收一个月呢!

\(1.\)虚树

\(2.\)KD-Tree

\(3.\)长链剖分

\(4.\)后缀数组

时间复杂度:倍增求法,复杂度 \(O(nlogn)\)

首先把 \(s\) 的每个后缀字典序排序。
\(sa[i]:\) 排名第 \(i\) 位的是第几个后缀(起始下标)。
\(rk[i]:\) 第 \(i\) 个(起始下标为 \(i\))的后缀的的排名。
\(height[i]:\) \(sa[i]\) 与 \(sa[i-1]\) 的最长公共前缀。

\(height\) 数组的求法:
假设所有后缀都已经排好序了,求 \(Lcp(i,j)\)
有:(\(i,j,k\) 均为排名,不作证明) $$Lcp(i,j) = Lcp(j,i)$$

\[Lcp(i,i) = len(i) \]

\[Lcp(i,j)=min(Lcp(i,k), Lcp(k,j)),i<=k<=j \]

\[height[i]=Lcp(i-1,i) \]

定义 \(h(i)=height[rk[i]]\),即起始下标为 \(i\) 的后缀与它排名前一的后缀的最长公共前缀
有:$$h(i)>=h(i - 1) - 1$$

\(5.\)后缀自动机

#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

template <class T> inline void read(T &x){
    x = 0; register char c = getchar(); register bool f = 0;
    while (!isdigit(c)) f ^= c == '-', c = getchar();
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
    if (f) x = -x;
}

template <class T> inline void print(T x){
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) print(x / 10);
    putchar('0' + x % 10);
}

const int N = 1e6 + 10;
int n, m;
char s[N];
int sa[N], x[N], y[N], c[N], rk[N], height[N];
//sa 排名数组,x第一关键字,y第二关键字,c每个关键字的个数,rk第i个后缀的排名

inline void get_sa(){
    for(int i = 1; i <= n; i ++) c[x[i] = s[i]] ++;//首先按照第一个字母为第一关键字排序,每个关键字的个数累加一下
    for(int i = 2; i <= m; i ++) c[i] += c[i - 1];//基数排序,统计小于等于每个关键字的个数
    for(int i = n; i; i --) sa[c[x[i]] --] = i;//从后往前求每个后缀的排名是多少 **从后往前可以使基数排序stable即元素相等不换顺序**
    for(int k = 1; k <= n; k <<= 1){
        int num = 0;
        for(int i = n - k + 1; i <= n; i ++) y[++ num] = i;//将后面没有第二关键字的后缀存下来  
        for (int i = 1; i <= n; i ++ )
            if (sa[i] > k)
                y[ ++ num] = sa[i] - k;
        //再次按第一关键字排序
        for (int i = 1; i <= m; i ++ ) c[i] = 0;
        for (int i = 1; i <= n; i ++ ) c[x[i]] ++ ;//求第一关键字出现数量
        for (int i = 2; i <= m; i ++ ) c[i] += c[i - 1];//前缀和
        for (int i = n; i; i -- ) sa[c[x[y[i]]] -- ] = y[i], y[i] = 0;//按第二关键字从后往前枚举
        swap(x, y);
        //离散化 
        x[sa[1]] = 1, num = 1;
        for (int i = 2; i <= n; i ++ )
            x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++ num;
        if (num == n) break;//排完了,所有的后缀都不一样了
        m = num;
    }   
}

inline void get_height(){
    for(int i = 1; i <= n; i ++) rk[sa[i]] = i;//排名是i的字符串排名是i (废话)
    for(int i = 1, k = 0; i <= n; i ++){
        if(rk[i] == 1) continue;//height[1] = 0
        if(k) k --;
        int j = sa[rk[i] - 1];
        while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) k ++;
        height[rk[i]] = k; //h(i)=k
    }
}

signed main(){
    scanf("%s", s + 1); //下标从1开始
    n = strlen(s + 1), m = 122;//值域,最大ASCII是z:122
    get_sa();
    get_height();
    for(int i = 1; i <= n; i ++) printf("%d ", sa[i]);
    puts("");
    for(int i = 1; i <= n; i ++) printf("%d ", height[i]);
    return 0;
}````

标签:长链,剖分,Lcp,后缀,height,int,sa,include
来源: https://www.cnblogs.com/William-Sg/p/16345884.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有