后缀数组(SA)
来源:互联网 发布:网络机柜全套 编辑:程序博客网 时间:2024/04/28 10:20
Preface
这其实并不是一个特别高深的算法,但是有的性质需要自己多推一下,慢慢理解
进入正题
SA和Rank
如果把每个后缀求出来,用快排之类的排序排是需要
显然没有充分利用后缀之间的关系
要求出两个数组
一般后缀排序有两种方法,倍增和
我们设
对于每个
显然有几个性质
sf(i,2k)<sf(j,2k) 当且仅当sf(i,k)<sf(j,k) 或sf(i,k)=sf(j,k) 并且sf(i+k,k)<sf(j+k,k) sf(i,2k)=sf(j,2k) 当且仅当sf(i,k)=sf(j,k) 并且sf(i+k,k)=sf(j+k,k)
设数组
那么完全可以利用
然而,在
于是每个
当然,初始时你需要对每一个字符做一遍排序,相当于
下面是构造的程序
#define fo(i,a,b) for(i=a;i<=b;i++)#define fod(i,a,b) for(i=a;i>=b;i--)void make(){ memset(ct,0,sizeof(ct)); int i,j,k,mx; fo(i,1,n) ct[rank[i]=st[i]]++;//这里用的是计数排序,为了方便,直接用ASCII码来比大小 fo(i,1,m) ct[i]+=ct[i-1]; fod(i,n,1) SA[ct[rank[i]]--]=i;//求出SA1 mx=m; for(j=1,k=0;k<n;j*=2,mx=k)//这里mx表示最大的Rank值,也就是不同的sf(i,k)数目(想想为什么),如果每一个字符串都不同,再排也就没有意义了,直接退出。 { int p=0; fo(i,n-j+1,n) s2[++p]=i; fo(i,1,n) if(SA[i]>j) s2[++p]=SA[i]-j;//第二关键字直接用上一次的SA计算,s2表示按照第二关键字排序的sf(i,2k) memset(ct,0,sizeof(ct)); fo(i,1,n) ct[rank[s2[i]]]++; fo(i,1,mx) ct[i]+=ct[i-1]; fod(i,n,1) SA[ct[rank[s2[i]]]--]=s2[i];//这里必须用downto,因为要把s2排在后面的放的尽量靠后(所以要--) r1[SA[1]]=k=1; fo(i,2,n) r1[SA[i]]=(rank[SA[i-1]]==rank[SA[i]]&&rank[SA[i-1]+j]==rank[SA[i]+j])?k:++k;//求新一轮的Rank,k代表当前到第几名,也就是不同的rank个数 fo(i,1,n) rank[i]=r1[i]; }}
至此,
最长公共前缀(LCP)
然而,往往光有
设
设
设
于是有一些非常好的性质
LCP(i,j)=min(LCP(i,k),LCP(k,j)),i<k≤j (LCP定理)
证法较为复杂,见
http://baike.baidu.com/link?url=nYOXOZ0cAcRFHtSczK6JI05hTsSRlQTiKyvPds90yB7CjooOQfeNI3W1nVk04mNd8-lr07ehEnZICh8R_LgOg_
于是可得出一个推论(因为取了min)
LCP(i,j)≥LCP(i,k),i≤j<k
用文字说明,就是排名更靠近的后缀的最长公共前缀更长(我感觉是比较显然的)
有了这两个东西,我们就可以用RMQ之类的算法快速求出任意两个后缀的最长公共前缀了
关键在于,如何快速的求出
我们设
有一个非常非常重要的性质
- 若
i>1 且Rank[i]>1 ,则有h[i]≥h[i−1]−1
因为百科上证明不容易理解,我讲一个容易理解的版本
如果有
那么
sf(i)<sf(j)等价于sf(i+1)<sf(j+1) lcp(i+1,j+1)=lcp(i,j)−1
第二条看似难以理解,实际上非常简单,由于
那么它也就显然成立了
有了这两条,我们来证明上面那个性质
若
若
设
则
根据上面的第二条
有
现在我们只需要证明
想起了之前的推论了吗?
因为
所以
那么
于是上面的性质得证
有了这个性质,我们就可以有
对于每一个
求出
每次判断看似是
下面是代码,真的非常短
void findh(){ height[1]=0; int i=0,j; fo(i,1,n) { if (rank[i]==1) continue; j=max(height[rank[i-1]]-1,0); while (st[i+j]==st[SA[rank[i]-1]+j]) j++; height[rank[i]]=j; }}
至此,大功告成
附上
http://blog.csdn.net/hzj1054689699/article/details/51398396
这里有一道
JZOJ[1598]文件修复,下面附上链接的题意和讲解
http://blog.csdn.net/hzj1054689699/article/details/51398299
- 后缀数组(SA)
- 后缀数组(SA)模版
- 后缀数组(SA倍增算法)
- SA后缀数组模板
- 后缀数组SA
- 【后缀数组sa学习小记】
- 后缀数组求rank数组,sa数组
- [sa][后缀数组]关于后缀数组的若干技巧
- SA后缀数组模板 文件修复
- Hdu-5769 Substring (SA后缀数组)
- 后缀数组—sa与rank
- BZOJ 题目1031: [JSOI2007]字符加密Cipher(后缀数组sa简单应用)
- 后缀数组的构造sa,rank和height数组
- 【BZOJ 1031】 [JSOI2007]字符加密Cipher 后缀数组sa数组
- 关于后缀数组的SA和Rank说明
- poj3581 Sequence(后缀数组sa的运用+离散化)
- SA(后缀数组)——倍增法模板
- 后缀数组【倍增计数排序求sa】poj1743
- OpenResty反爬虫
- ShareSDK各社交平台申请APPkey 的网址及申请流程汇总
- android获取gallery中的图片以及调用系统相机
- 终结Android项目的R文件报错问题
- lintcode ----搜索插入位置
- 后缀数组(SA)
- iOS打印功能
- gradle版本 gradle各种版本下载提供
- 小图标的位置实现
- JAVA三次层架构和MVC
- Android 中的线程形态 -- AsyncTask,HandlerThread,IntentService
- Android 错误提示:Conversion to Dalvik format failed with error 1 解决办法
- JNI 与c++通信出现乱码解决
- Unity3d物体模型(实现旋转缩放平移自动旋转)