后缀数组(SA)

在看完洛谷大佬的

最详细讲解

以后,我还是不能说没有完全不懂,所以干脆自己写一篇后缀数组详解,造福后人(QAQ)
本篇讲解引用例子和图片来自某不知名视频资源的大佬,如有知道大佬姓名,会立刻回来标注的。


开始之前,先要了解这些数组是干嘛的,一定要记好。
image

一下以长度为8的字符串aabaaaab为例子,进行讲解
(好的,直接面向代码开始讲解)

一下以长度为8的字符串aabaaaab为例子,进行讲解
(好的,直接面向代码开始讲解)

这里是定义

const int N=2000010;
char s[N];
int n,m;//n为后缀个数, m为桶的个数
int x[N],y[N],c[N],sa[N],rk[N],height[N];
//桶数组x[i],辅助数组y[i],计数数组c[i]

这是按第一关键字(首字母)排序,对理解后面的代码很重要

//按第一个字母排序
  for(i=1;i<=n;i++)c[x[i]=s[i]]++;
  for(i=1;i<=m;i++)c[i]+=c[i-1];
  for(i=n;i;i--)sa[c[x[i]]--]=i;

第一排代码,先执行n次复制\(x[i]\)\(c[i]\)\(x[i]\)存放第i个后缀的第k为的字典序

\(c[i]\)表示存放字典序为i的后缀个数

\(y[i]\)备份上一次的\(sa[i]\)

其中核心语句

sa[c[x[i]]--]=i;

最为关键,一个for按后缀编号为序把每一个后缀的排序号给计算出来了。

淦,刚刚看到洛谷的题解提交通道以及关闭了,那我码这么多字干嘛!!!

直接上图,图中样例里有些行列没有标明是数组编号,还是数组存值,需要自己区分清楚,好好根据例子理解一下代码过程

还要去提升人身阅历了,拜拜~(QAQ)
image
image
image
image

点击查看代码
// Luogu P3809 【模板】后缀排序
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

const int N=2000010;
char s[N];
int n,m;//n为后缀个数, m为桶的个数
int x[N],y[N],c[N],sa[N],rk[N],height[N];
//桶数组x[i],辅助数组y[i],计数数组c[i]
void get_sa(){
  int i,j,k;
  //按第一个字母排序
  for(i=1;i<=n;i++)c[x[i]=s[i]]++;
  for(i=1;i<=m;i++)c[i]+=c[i-1];
  for(i=n;i;i--)sa[c[x[i]]--]=i;
  for(k=1;k<=n;k<<=1){ //logn轮
    //按第二关键字排序
    memset(c,0,sizeof(c));
    for(i=1;i<=n;i++)y[i]=sa[i];
    for(i=1;i<=n;i++)c[x[y[i]+k]]++;
    for(i=1;i<=m;i++)c[i]+=c[i-1];
    for(i=n;i;i--)sa[c[x[y[i]+k]]--]=y[i];
    //按第一关键字排序
    memset(c,0,sizeof(c));
    for(i=1;i<=n;i++)y[i]=sa[i];
    for(i=1;i<=n;i++)c[x[y[i]]]++;
    for(i=1;i<=m;i++)c[i]+=c[i-1];
    for(i=n;i;i--)sa[c[x[y[i]]]--]=y[i];
    //把后缀放入桶数组
    for(i=1;i<=n;i++)y[i]=x[i];
    for(m=0,i=1;i<=n;i++)
      if(y[sa[i]]==y[sa[i-1]]&&
        y[sa[i]+k]==y[sa[i-1]+k])x[sa[i]]=m;
      else x[sa[i]]=++m;
    if(m==n)break;//已排好    
  }  
}
void get_height(){
  int i,j,k;
  for(i=1;i<=n;i++)rk[sa[i]]=i;
  for(i=1,k=0;i<=n;i++){ //枚举后缀i
    if(rk[i]==1)continue;//第一名height为0
    if(k)k--;//上一个后缀的height值减1
    int j=sa[rk[i]-1];//找出后缀i的前邻后缀j
    while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
    height[rk[i]]=k;
  }
}
int main(){
  scanf("%s",s+1);
  n=strlen(s+1); m=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;
}

溜了,溜了

posted @ 2023-05-02 16:19  alloverzyt  阅读(60)  评论(1编辑  收藏  举报