后缀数组入门学习(1):倍增算法基础
2017-07-03 17:19
513 查看
满心欢喜来了后缀数组,但是发现自己很懵……
后缀数组倍增算法的板子并不长,但理解起来相对很困难。
后缀的含义自然不用说了……为了简洁,我们设后缀i为从第i位开始的后缀。
需要这样几个数组:
char s[],存字符串(废话),int sa[](本体),xx[](中转数组,存rank),yy[](中转数组,存编号),c[](基排用)
然后我们需要预备知识:基数排序
具体理论就不说了,可以参考百度百科;
形象一点:找了一堆桶,把每个数/字符放在对应的桶里,然后按桶的编号挨个取出来,排好,就是基数排序。
在代码实现上c数组就是我们的桶,c[i]表示“字符值小于等于i的字符个数”
而对于字符值恰好等于i的那个字符,它的排名就是c[i](由于它是小于等于i的最大一个字符),然后我们把c[i]-=1即可
代码长这样:
接下来,我们就可以介绍本体代码了:
首先,我们对每个字符排序,这样我们会得到每个字符的排名。可能有同学有疑问,按照基排的话,相同字母的排名就不同了怎么办?后面我们还会有去重的部分,所以我们中转数组这样不重复的打也没事。在这之后,我们给每个后缀的前两个字符排序(没有第二个字符的按照0处理,0最小,以后也是这样操作)。这相当于给一些二元组排序。利用我们刚刚第一次给字母排序的结果,很容易实现这次排序(我们在从第二次开始每次排序后面都会进行一次去重)。
这之后,我们对每个后缀的前4个字符排序。由于对于后缀i和后缀j,给他们的前4个字符排序相当于“比较i和j的前两个字符”和“比较i+2和j+2的前两个字符”,而我们已经给前两个字符排好序,所以这样就可以利用前面排序的结果来操作。
不断这样操作,当每个后缀的排名都不同时,就可以跳出了。
代码见下:
后缀数组倍增算法的板子并不长,但理解起来相对很困难。
后缀的含义自然不用说了……为了简洁,我们设后缀i为从第i位开始的后缀。
需要这样几个数组:
char s[],存字符串(废话),int sa[](本体),xx[](中转数组,存rank),yy[](中转数组,存编号),c[](基排用)
然后我们需要预备知识:基数排序
具体理论就不说了,可以参考百度百科;
形象一点:找了一堆桶,把每个数/字符放在对应的桶里,然后按桶的编号挨个取出来,排好,就是基数排序。
在代码实现上c数组就是我们的桶,c[i]表示“字符值小于等于i的字符个数”
而对于字符值恰好等于i的那个字符,它的排名就是c[i](由于它是小于等于i的最大一个字符),然后我们把c[i]-=1即可
代码长这样:
memset(c,0,sizeof(c)); for(int i=0;i<n;i++)c[x[i]=s[i]]++;//这里是一开始初始化赋值的地方 for(int i=1;i<m;i++)c[i]+=c[i-1]; for(int i=n-1;~i;i--)sa[--c[x[i]]]=i;
接下来,我们就可以介绍本体代码了:
首先,我们对每个字符排序,这样我们会得到每个字符的排名。可能有同学有疑问,按照基排的话,相同字母的排名就不同了怎么办?后面我们还会有去重的部分,所以我们中转数组这样不重复的打也没事。在这之后,我们给每个后缀的前两个字符排序(没有第二个字符的按照0处理,0最小,以后也是这样操作)。这相当于给一些二元组排序。利用我们刚刚第一次给字母排序的结果,很容易实现这次排序(我们在从第二次开始每次排序后面都会进行一次去重)。
这之后,我们对每个后缀的前4个字符排序。由于对于后缀i和后缀j,给他们的前4个字符排序相当于“比较i和j的前两个字符”和“比较i+2和j+2的前两个字符”,而我们已经给前两个字符排好序,所以这样就可以利用前面排序的结果来操作。
不断这样操作,当每个后缀的排名都不同时,就可以跳出了。
代码见下:
char s ; int sa ,rank ,xx ,yy ,c ,n; inline void get_sa(int m)//m为最大的字符值 { int *x=xx,*y=yy; memset(c,0,sizeof(c)); for(int i=0;i<n;i++)c[x[i]=s[i]]++; for(int i=1;i<m;i++)c[i]+=c[i-1]; for(int i=n-1;~i;i--)sa[--c[x[i]]]=i; for(int k=1,p=0;k<=n;k<<=1,p=0) { for(int i=n-k;i<n;i++)y[p++]=i; for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; memset(c,0,sizeof(c)); for(int i=0;i<n;i++)c[x[y[i]]]++; for(int i=1;i<m;i++)c[i]+=c[i-1]; for(int i=n-1;~i;i--)sa[--c[x[y[i]]]]=y[i]; swap(x,y); p=1,x[sa[0]]=0; for(int i=1;i<n;i++) x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?p-1:p++; if(p>=n-1)return;m=p; } }
相关文章推荐
- 后缀数组学习笔记——罗穗骞倍增算法代码
- 后缀数组--学习笔记(倍增算法)
- 后缀数组之倍增算法——学习笔记
- 后缀数组学习笔记——罗穗骞倍增算法代码
- 数组02 - 零基础入门学习C语言24
- 数组05 - 零基础入门学习C语言27
- 关于数组的认识02 - 零基础入门学习Delphi16
- 后缀数组 倍增算法 代码详解
- 数组03 - 零基础入门学习C语言25
- 数组09 - 零基础入门学习C语言31
- 关于数组的认识03 - 零基础入门学习Delphi17
- 关于数组的认识01 - 零基础入门学习Delphi15
- 后缀数组-倍增算法模板
- 数组05 - 零基础入门学习C语言27
- 关于数组的认识03 - 零基础入门学习Delphi17
- 数组01 - 零基础入门学习C语言23
- 数组04 - 零基础入门学习C语言26
- 数组07 - 零基础入门学习C语言29
- 数组07 - 零基础入门学习C语言29
- 后缀数组——罗穗骞倍增算法详细注释