• 学习后缀数组笔记


      学习自:https://www.cnblogs.com/victorique/p/8480093.html

    后缀:是字符串的一个特殊子串,以s的第i个字符为第一个元素的后缀为suff(i)。

    后缀数组:后缀数组sa[i]就表示排名为i的后缀的起始位置的下标。

    相反映射:rk[i]就表示起始位置的下标为i的后缀的排名。

    倍增法+基数排序实现后缀数组O(nlogn)排序:思想是先对首字符排序(重复),在对相邻相加再排序,直到所有排名不同即可。(需进一步理解)

    最长公共前缀:

    我们定义LCP(i,j)为suff(sa[i])与suff(sa[j])的最长公共前缀。

    1. LCP(i,j)=LCP(j,i);
    2. LCP(i,i)=len(sa[i])=n-sa[i]+1;

    LCP引理

      LCP(i,k)=min(LCP(i,j),LCP(j,k)) 对于任意1<=i<=j<=k<=n

    LCP定理

      LCP(i,k)=min(min(LCP(j,j-1))) 对于1<i<=j<=k<=n

    重要定理:

    我们设height[i]为LCP(i,i-1),1<i<=n,显然height[1]=0;

    由LCP Theorem可得,LCP(i,k)=min(height[j]) i+1<=j<=k

    设h[i]=height[rk[i]],同样的,height[i]=h[sa[i]];

    有:  

      h[i]>=h[i-1]-1;

    证明:设k=rk[i-1]-1.

    则有h[i-1] = height[rk[i-1]]=min(LCP(k-1,k-2) )

    rk[i-1]<rk[sa[k]]

    rk[i]<rk[sa[k]+1]

    LCP( i,rk[sa[k]+1] )=h[i-1]-1;

    h[i]>=h[i-1];

    例题:luogu3809 后缀排序

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #define rint register int
     5 #define inv inline void
     6 #define ini inline int
     7 #define maxn 1000050
     8 using namespace std;
     9 char s[maxn];
    10 int y[maxn],x[maxn],c[maxn],sa[maxn],rk[maxn],height[maxn],wt[30];
    11 int n,m;
    12 inv putout(int x) {
    13     if(!x) {
    14         putchar(48);
    15         return;
    16     }
    17     rint l=0;
    18     while(x) wt[++l]=x%10,x/=10;
    19     while(l) putchar(wt[l--]+48);
    20 }
    21 inv get_SA() {
    22     for (rint i=1; i<=n; ++i) ++c[x[i]=s[i]];
    23 //c数组是桶
    24 //x[i]是第i个元素的第一关键字
    25     for (rint i=2; i<=m; ++i) c[i]+=c[i-1];
    26 //做c的前缀和,我们就可以得出每个关键字最多是在第几名
    27     for (rint i=n; i>=1; --i) sa[c[x[i]]--]=i;
    28     for (rint k=1; k<=n; k<<=1) {
    29         rint num=0;
    30         for (rint i=n-k+1; i<=n; ++i) y[++num]=i;
    31 //y[i]表示第二关键字排名为i的数,第一关键字的位置
    32 //第n-k+1到第n位是没有第二关键字的 所以排名在最前面
    33         for (rint i=1; i<=n; ++i) if (sa[i]>k) y[++num]=sa[i]-k;
    34 //排名为i的数 在数组中是否在第k位以后
    35 //如果满足(sa[i]>k) 那么它可以作为别人的第二关键字,就把它的第一关键字的位置添加进y就行了
    36 //所以i枚举的是第二关键字的排名,第二关键字靠前的先入队
    37         for (rint i=1; i<=m; ++i) c[i]=0;
    38 //初始化c桶
    39         for (rint i=1; i<=n; ++i) ++c[x[i]];
    40 //因为上一次循环已经算出了这次的第一关键字 所以直接加就行了
    41         for (rint i=2; i<=m; ++i) c[i]+=c[i-1]; //第一关键字排名为1~i的数有多少个
    42         for (rint i=n; i>=1; --i) sa[c[x[y[i]]]--]=y[i],y[i]=0;
    43 //因为y的顺序是按照第二关键字的顺序来排的
    44 //第二关键字靠后的,在同一个第一关键字桶中排名越靠后
    45 //基数排序
    46         swap(x,y);
    47 //这里不用想太多,因为要生成新的x时要用到旧的,就把旧的复制下来,没别的意思
    48         x[sa[1]]=1;
    49         num=1;
    50         for (rint i=2; i<=n; ++i)
    51             x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]) ? num : ++num;
    52 //因为sa[i]已经排好序了,所以可以按排名枚举,生成下一次的第一关键字
    53         if (num==n) break;
    54         m=num;
    55 //这里就不用那个122了,因为都有新的编号了
    56     }
    57     for (rint i=1; i<=n; ++i) putout(sa[i]),putchar(' ');
    58 }
    59 inv get_height() {
    60     rint k=0;
    61     for (rint i=1; i<=n; ++i) rk[sa[i]]=i;
    62     for (rint i=1; i<=n; ++i) {
    63         if (rk[i]==1) continue;//第一名height为0
    64         if (k) --k;//h[i]>=h[i-1]+1;
    65         rint j=sa[rk[i]-1];
    66         while (j+k<=n && i+k<=n && s[i+k]==s[j+k]) ++k;
    67         height[rk[i]]=k;//h[i]=height[rk[i]];
    68     }
    69     putchar(10);
    70     for (rint i=1; i<=n; ++i) putout(height[i]),putchar(' ');
    71 }
    72 int main() {
    73     gets(s+1);
    74     n=strlen(s+1);
    75     m=122;
    76 //因为这个题不读入n和m所以要自己设
    77 //n表示原字符串长度,m表示字符个数,ascll('z')=122
    78 //我们第一次读入字符直接不用转化,按原来的ascll码来就可以了
    79 //因为转化数字和大小写字母还得分类讨论,怪麻烦的
    80     get_SA();
    81 //get_height();
    82 }
    View Code
  • 相关阅读:
    阿里云 k8s 部署 Spring Cloud Alibaba 微服务实践 (四) 自动化部署
    阿里云 k8s 部署 Spring Cloud Alibaba 微服务实践 (三) 服务观测
    阿里云 k8s 部署 Spring Cloud Alibaba 微服务实践 (二) 部署微服务程序
    阿里云 k8s 部署 Spring Cloud Alibaba 微服务实践 (一) 部署 Nacos
    C++知识点
    libmkl 学习笔记
    基于tesseract-OCR进行中文识别
    poco编译与运行
    Linux下的I/O复用与epoll详解(转载)
    高并发网络编程之epoll详解(转载)
  • 原文地址:https://www.cnblogs.com/bestefforts/p/9412282.html
Copyright © 2020-2023  润新知