后缀数组学习笔记
来源:互联网 发布:赛睿鼠标sensei淘宝 编辑:程序博客网 时间:2024/06/06 12:36
【吐槽】学了好久的后缀数组,看了各个大神博客,还是没懂= =,看起来好难的一个东西【一堆堆for】。主要还是自己码代码的能力太弱了。。。。
后缀数组,顾名思义,一定与后缀有关。后缀数组简称sa,sa[i]表示在字符串s的所有后缀中,排名第i的后缀的首字母在字符串中的位置。【排名从0开始】
比如,对于字符串"ababa",排名第一的后缀是最后一个a,那么sa[0]=4.
我们要做的事情,就是求出这个数组,怎么求呢?
观察下面这张图片
利用倍增的思想,我们先按每个后缀的第一个字符排序,再按每个后缀的前2个字符排序,再按每个后缀的前4个字符排序......直到没有相同排名为止
具体排序如何实现?
利用之前的排序,4个字符的排序其实可以转化为两个2个字符的排序,任意一次排序都是一个双关键字排序。
我们就可以利用基数排序很快的进行排序。
什么是基数排序?
基数排序遵循这样一个事实:按关键字对排序结果的的决定性从弱到强进行排序,最后的结果一定符合排序要求。
然后我们拿一个桶,装下出现的每一种数,然后再利用桶的前缀和来求出每个位的排序
举个例子:
3 4 6 82 99
我们知道,数字排序可以看作多个关键字排序:个位、十位、百位、千位......其中越高位决定性越强。
于是我们从个位开始排序【由于每个数都是0~9之间的,所以我们只需要十个桶】:
每个桶表示这个数字有多少个,到最后结果应该是这样的:
0 0 1 1 1 0 1 0 0 1
[ 0 1 2 3 4 5 6 7 8 9 ]
然后我们求出前缀和,表示不大于这个数的数有多少个
0 0 1 2 3 3 4 4 4 5
利用前缀和,我们就能很快得到排序
利用基数排序,我们就可以实现后缀数组啦:
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=100005;char s[maxn];int sa[maxn],t1[maxn],t2[maxn],buc[maxn],m='z'+1,n;void solve(){int *x=t1,*y=t2;n=strlen(s);for(int i=0;i<m;i++) buc[i]=0;for(int i=0;i<n;i++) buc[x[i]=s[i]]++;for(int i=1;i<m;i++) buc[i]+=buc[i-1];for(int i=n-1;i>=0;i--) sa[--buc[x[i]]]=i;for(int k=1;k<=n;k<<=1){int 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;//排序第一关键字for(int i=0;i<m;i++) buc[i]=0;for(int i=0;i<n;i++) buc[x[y[i]]]++;for(int i=1;i<m;i++) buc[i]+=buc[i-1];for(int i=n-1;i>=0;i--) sa[--buc[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]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k] ? p-1:p++;//排名各不相同,排序完成,直接退出if(p>=n) break;m=p;}}int main(){cin>>s;solve();for(int i=0;i<n;i++) printf("%d ",sa[i]+1);cout<<endl;return 0;}
2017.9.23添加
BZOJ 1031字符加密Cipher
题解
拆环成链,求后缀排序,然后按排名顺序将前一个字符输出即可
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define LL long long intusing namespace std;const int maxn=1000005,maxm=1000000,INF=2000000000,P=1000000007;inline int read(){int out=0,flag=1;char c=getchar();while(c<48||c>57) {if(c=='-') flag=-1;c=getchar();}while(c>=48&&c<=57){out=out*10+c-48;c=getchar();}return out*flag;}char s[maxn];int buc[maxm],t1[maxn],t2[maxn],sa[maxn],m=0,n,N;void init(){scanf("%s",s);N=strlen(s);char t='\0';for(int i=0;i<N;i++){s[N+i]=s[i];t=max(t,s[i]);}m=t+1;n=2*N;}void solve(){int *x=t1,*y=t2;for(int i=0;i<m;i++) buc[i]=0;for(int i=0;i<n;i++) buc[x[i]=s[i]]++;for(int i=1;i<m;i++) buc[i]+=buc[i-1];for(int i=n-1;i>=0;i--) sa[--buc[x[i]]]=i;for(int k=1;k<=n;k<<=1){int 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;}for(int i=0;i<m;i++) buc[i]=0;for(int i=0;i<n;i++) buc[x[y[i]]]++;for(int i=1;i<m;i++) buc[i]+=buc[i-1];for(int i=n-1;i>=0;i--) sa[--buc[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]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k] ? p-1:p++;if(p>=n) return;m=p;}}void print(){for(int i=0;i<n;i++){if(sa[i]<N){printf("%c",s[sa[i]+N-1]);}}printf("\n");}int main(){init();solve();print();return 0;}
阅读全文
0 0
- 后缀数组 学习笔记
- 后缀数组学习笔记
- 学习笔记----后缀数组
- 后缀数组学习笔记
- 后缀数组学习笔记
- 后缀数组学习笔记
- 后缀数组学习笔记
- 后缀数组学习笔记
- 后缀数组学习笔记
- 后缀数组 学习笔记
- 后缀数组学习笔记
- 后缀数组(SuffixArray) 学习笔记
- 【字符串数据结构后缀系列Part1】后缀数组学习笔记
- 后缀数组--学习笔记(倍增算法)
- 后缀数组学习笔记【详解|图】
- 《黑书》后缀数组学习笔记.
- |算法讨论|后缀数组 学习笔记
- suffix_array(后缀数组) 学习笔记
- js 数组
- bzoj 2431: [HAOI2009]逆序对数列
- Java多线程学习(吐血超详细总结)
- Zookeeper工具类的封装
- 机器学习降维算法二:LDA(Linear Discriminant Analysis)
- 后缀数组学习笔记
- 笔记13--js基础知识-函数
- CentOS7安装crtmpserver搭建在线直播平台(crtmpserver开机启动、rtmpd、rtmpserver、rtmp直播)
- CAD编辑之创建表格、新建空白图纸以及创建和粘贴图块
- Third Write
- Java多线程基础--05之 线程等待与唤醒
- 如何动态的制作级联菜单(上)
- Jquery获取元素距离文档顶部的距离
- 微擎架构