首页 > 编程语言 > 详细

后缀数组

时间:2019-09-16 20:19:00      阅读:92      评论:0      收藏:0      [点我收藏+]

 先粘上我入门时看的博客:

https://www.cnblogs.com/jinkun113/p/4743694.html

https://www.cnblogs.com/victorique/p/8480093.html

https://www.cnblogs.com/jinkun113/p/4743694.html

声明:以下部分内容摘自该博客,仅供个人复习时用


 

 

 

后缀数组是让人蒙逼的有力工具! 后缀数组是处理字符串的有力工具,可以解决很多关于字符串的问题

 

 注意:后缀数组并不是一种算法,而是一种思想。

实现它的方法主要有两种:倍增法O(nlogn) 和 DC3法O(n)

其中倍增法除了仅仅在时间复杂度上不占优势之外,其他的方面例如编程难度,空间复杂度,常数等都秒杀DC3法

建议深入理解倍增法,并能熟练运用(起码8分钟内写出来&&没有错误)。DC3法只做了解。

 

 

例题+板子:

https://www.luogu.org/problem/P3809

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <iostream>
 4 #include <string>
 5 #include <math.h>
 6 #include <algorithm>
 7 #include <vector>
 8 #include <stack>
 9 #include <queue>
10 #include <set>
11 #include <map>
12 #include <math.h>
13 const int INF=0x3f3f3f3f;
14 typedef long long LL;
15 const int mod=1e9+7;
16 const int maxn=1e6+10;
17 using namespace std;
18 
19 char str[maxn];
20 int sa[maxn];//排名为i的后缀的位置
21 int rk[maxn];//从第i个位置开始的后缀的排名
22 int C[maxn];//C数组表示桶,统计每种元素出现了多少次,辅助基数排序 
23 int X[maxn];//X[i]是第i个元素的第一关键字的字典排名 
24 int Y[maxn];//Y[i]表示在第二关键字中排名为i的数,其第一关键字的位置
25 
26 int n,m;
27 void Get_SA()
28 {
29     for(int i=1;i<=n;i++)
30         C[X[i]=str[i]]++;
31     for(int i=2;i<=m;i++)//做C的前缀和,我们就可以得出每个关键字最多是在第几名
32         C[i]+=C[i-1];
33     for(int i=n;i>=1;i--)
34         sa[C[X[i]]--] = i;
35     
36     for(int k = 1; k <= n; k <<= 1)
37     {//k:当前倍增的长度,k = x表示已经求出了长度为x的后缀的排名,现在要更新长度为2x的后缀的排名
38         int num=0; //num仅仅是一个计数器,示不同的后缀的个数,很显然原字符串的后缀都是不同的,因此num=n时可以退出循环
39         
40         for(int i=n-k+1;i<=n;i++)//第n-k+1到第n位是没有第二关键字的 所以排名在最前面
41             Y[++num]=i;
42         for(int i=1;i<=n;i++)
43             if(sa[i]>k)
44                 Y[++num]=sa[i]-k;
45 //排名为i的数 在数组中是否在第k位以后,即满足(sa[i]>k),那么它可以作为别人的第二关键字,就把它的第一关键字的位置添加进Y就行了
46 //所以i枚举的是第二关键字的排名,第二关键字靠前的先入队
47 
48         for(int i=1;i<=m;i++)
49             C[i]=0;//初始化C桶
50         for(int i=1;i<=n;i++)
51             C[X[Y[i]]]++;
52         //因为上一次循环已经算出了这次的第一关键字 所以直接加就行了
53         for(int i=1;i<=m;i++)
54             C[i]+=C[i-1];//第一关键字排名为1~i的数有多少个
55         for(int i=n;i>=1;i--)
56             sa[C[X[Y[i]]]--]=Y[i];
57 //因为Y的顺序是按照第二关键字的顺序来排的,第二关键字靠后的,在同一个第一关键字桶中排名越靠后
58 
59         swap(X,Y);//这里不用想太多,因为要生成新的X时要用到旧的,就把旧的复制下来,没别的意思
60         num=1;
61         X[sa[1]]=1;
62         for(int i=2;i<=n;i++)//因为sa[i]已经排好序了,所以可以按排名枚举,生成下一次的第一关键字X; 
63             X[sa[i]]=(Y[sa[i]]==Y[sa[i-1]] && Y[sa[i]+k]==Y[sa[i-1]+k]) ? num : ++num;
64         if(num==n)
65             break;
66         m=num;//这里就不用那个122了,因为都有新的编号了
67     }
68 }
69 
70 int main()
71 {
72     gets(1+str);
73     n=strlen(1+str);
74     m=122;
75     Get_SA();
76     for(int i=1;i<=n;i++)
77     {
78         if(i==1)
79             printf("%d",sa[i]);
80         else
81             printf(" %d",sa[i]);
82     }
83     printf("\n");
84     return 0;
85 }

 

 

 

 

经典应用

两个后缀的最大公共前缀

lcp(x,y)=min(heigh[xy]), 用rmq维护,O(1)查询

可重叠最长重复子串

Height数组里的最大值

不可重叠最长重复子串 POJ1743

首先二分答案x,对height数组进行分组,保证每一组的minheight>=x>=x

依次枚举每一组,记录下最大和最小长度,多sa[mx]sa[mi]>=x那么可以更新答案

本质不同的子串的数量

枚举每一个后缀,第i个后缀对答案的贡献为lensa[i]+1height[i]

 

 

 

 

 


 声明:我博客里有大量的从别的博客复制过来的代码,分析,以及理解,但我一律会在文章后面标记原博客大佬博客地址,其中部分会加以连接。 绝无抄袭的意思,只是为了我在复习的时候找博客方便。 如有原作者对此有不满,请在博客留言,我一定会删除该博文。

后缀数组

原文:https://www.cnblogs.com/jiamian/p/11503808.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!