关于后缀数组的一点想法

  后缀数组大概就是用后缀排名来搞一些事情,因为字符串中的每一个子串都可看做某一后缀的前缀

  可用倍增法求出后缀排名

一、数组意义(对于字符串 s)

  sa[i]:排名为i的后缀的开头在s中的位置

  height[i]:排名为i的后缀和排名为i-1的后缀的LCP(最长公共前缀)

  c[]:用于基数排序,统计前缀和

  rank[i]:以s[i]开头的后缀的排名  显然 rank[sa[i]]=i    sa[rank[i]]=i

二、求sa[]具体思路

  1.用倍增法构造第一、第二关键词,第一关键词小的排在前,第一关键词相同的 第二关键词小的排在前。

  2.优化:如果后缀长度枚举到某一大小时,每个后缀的排名彼此不同,那么可以直接退出,道理很显然

三、求height[]具体思路

  先求出rank[i]

  看height[]的定义,知道应取suffix(sa[rank[i-1]])和suffix(i)的LCP

  只要找到suffix(sa[rank[i-1]])的开头j,暴力枚举便可

  此处有一个小优化(详情参见http://www.cnblogs.com/LLGemini/p/4771235.html

     h[]即为height[]

   对于i>1 且Rank[i]>1,一定有h[i]≥h[i-1]-1。(这条性质要好好理解!)

   证明:设suffix(k)是排在suffix(i-1)前一名的后缀,它们的最长公共前缀是h[i-1]。

那么suffix(k+1)将排在suffix(i)的前面(这里要求h[i-1]>1,如果h[i-1]≤1,原式显然成立)并且suffix(k+1)和suffix(i)的最长公共前缀是h[i-1]-1,

所以suffix(i)和在它前一名的后缀的最长公共前缀至少是h[i-1]-1。

按照h[1],h[2],……,h[n]的顺序计算,并利用h 数组的性质,时间复杂度可以降为O(n)。

 

四、注意事项

  构建sa[]时,传4个参进入函数,设原字符串为s,设s的长度为n,s中最大字符的大小为m

  build_sa(s[],sa[],n+1,m)

  传n+1而不传n的原因是在s的末尾补上了一个 “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++;

    如果 x[idx1]==x[idx2](注意idx1!=idx2), 说明以 idx1或 idx2 开头的长度为 len 的字符串肯定不包括字符 x[n-1] , 所以调用变量 sa[idx1+len] 和 sa[idx2+len] 不会导致数组越界, 这样就不需要做特殊判断.

  

  完美解决了!

代码:  

 1 int sa[N],rk[N],h[N],c[N],r[N],wa[N],wb[N],sp[N],n,k;
 2
 3 void get_sa(int *r,int *sa,int n,int m){
 4     int *x=wa,*y=wb;//都是辅助变量
 5     for(int i=0;i<n;i++)c[x[i]=r[i]]++;
 6     for(int i=1;i<m;i++)c[i]+=c[i-1];
 7     for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
 8     for(int k=1;k<=n;k<<=1){
 9         int p=0;
10         for(int i=n-k;i<n;i++)y[p++]=i;
11         for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
12
13         for(int i=0;i<m;i++)c[i]=0;
14         for(int i=0;i<n;i++)c[x[i]]++;
15         for(int i=1;i<m;i++)c[i]+=c[i-1];
16         for(int i=n-1;~i;i--)sa[--c[x[y[i]]]]=y[i];
17
18         swap(x,y);//x,y是指针,直接互换
19         p=1;x[sa[0]]=0;
20         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++;
21         if(p>=n)break;
22         m=p;//优化:最多有p个元素,下一次最大值为p
23     }
24 }
25
26 void get_h(){
27     int k=0,mh=-1;
28     for(int i=0;i<n;i++)rk[sa[i]]=i;
29     for(int i=0;i<n;i++){
30         if(k)k--;
31         int j=sa[rk[i]-1];
32         while(r[i+k]==r[j+k])k++;
33         h[rk[i]]=k;
34     }
35 }

  

  2017-06-02 20:58:43

时间: 2024-10-12 04:07:08

关于后缀数组的一点想法的相关文章

hdu 4416 Good Article Good sentence(后缀数组&amp;思维)

Good Article Good sentence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2308    Accepted Submission(s): 649 Problem Description In middle school, teachers used to encourage us to pick up pre

后缀数组(一堆干货)

其实就是将两篇论文里的东西整合在了一起,并且提供了一个比较好理解的板. 后缀数组 字符串:一个字符串S是将n个字符顺次排列形成的数组,n称为S的长度,表示为len(S).S的第i个字符表示为S[i]. 子串:字符串S的子串S[i…j],i<=j,表示从S串中从i到j这一段,也就是顺次排列S[i],S[i+1],……,S[j]形成的字符串. 后缀:后缀是指从某个位置i开始到整个字符串末尾结束的一个特殊子串.字符串S的从i开关的后缀表示为Suffix(S,i),也就是Suffix(S,i)=S[i…

_bzoj1031 [JSOI2007]字符加密Cipher【后缀数组】

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1031 保存后缀数组模版. 其实如果数据范围小一点,或者空间限制再大一点,或者甚至只要字母表再小一点就可以使用后缀自动机了,只可惜空间不允许,就用后缀数组! 其实还是不是很理解代码,是否该当成黑盒代码背下来呢? #include <cstdio> #include <cstring> #include <algorithm> const int maxn = 200

bzoj 2251: [2010Beijing Wc]外星联络 后缀数组

2251: [2010Beijing Wc]外星联络 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 424  Solved: 232[Submit][Status][Discuss] Description 小 P 在看过电影<超时空接触>(Contact)之后被深深的打动,决心致力于寻 找外星人的事业.于是,他每天晚上都爬在屋顶上试图用自己的收音机收听外星 人发来的信息.虽然他收听到的仅仅是一些噪声,但是他还是按照这些噪声的高 低电平将接收到

HDU 1403 Longest Common Substring(后缀数组,最长公共子串)

hdu题目 poj题目 参考了 罗穗骞的论文<后缀数组——处理字符串的有力工具> 题意:求两个序列的最长公共子串 思路:后缀数组经典题目之一(模版题) //后缀数组sa:将s的n个后缀从小到大排序后将 排序后的后缀的开头位置 顺次放入sa中,则sa[i]储存的是排第i大的后缀的开头位置.简单的记忆就是“排第几的是谁”. //名次数组rank:rank[i]保存的是suffix(i){后缀}在所有后缀中从小到大排列的名次.则 若 sa[i]=j,则 rank[j]=i.简单的记忆就是“你排第几”

你可以在 Python 中轻易创建一个后缀数组

我想告诉你一个关于后缀数组的故事.在一段时间里,我正在西雅图的一家公司面试,当时好奇的是如何最有效地创建一个用于可执行二进制文件的diff.我的研究给我带来了后缀数组和后缀树.后缀数组只是,将字符串的所有后缀排序,储存到有序列表中.后缀树是类似的,但是比列表更像BSTree.这些算法相当简单,一旦你进行了排序操作,它们就具有很快的性能.他们解决的问题是,找到两个字符串之间最长的公共子串(或者在这种情况下是字节列表). 你可以在 Python 中轻易创建一个后缀数组: >>> magic

【集训第五天&#183;后缀数组】哇哈哈哈哈~

前面放了几天假(虽然我们是在补课),所以懒得写博客.补课期间学习了网络流,后面写专题总结.网络流主要是建模,慢慢搞. 后缀数组这个东西非常难搞,当初老师讲的时候  我(lao)们(shi) 水了2h,讲完还是一脸懵逼.虽然思路是理解了,但是完全不知道代码是什么意思.cnm 然后某一天晚上我没交手机,坐在寝室的床上从23点啃代码啃到00:30,终于是明白了..cnm ------------------------------------------------------------------

BZOJ 1692: [Usaco2007 Dec]队列变换 [后缀数组 贪心]

1692: [Usaco2007 Dec]队列变换 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1383  Solved: 582[Submit][Status][Discuss] Description FJ打算带他的N(1 <= N <= 30,000)头奶牛去参加一年一度的“全美农场主大奖赛”.在这场比赛中,每个参赛者都必须让他的奶牛排成一列,然后领她们从裁判席前依次走过. 今年,竞赛委员会在接受队伍报名时,采用了一种新的登记规则:他们把所

POJ 1509 后缀数组

点击打开链接 题意:给个串问最小的后缀,但是这个串是个环 思路:是个环也好处理,那么就将串复制一遍加到后面就行了,然后求出后缀数组和高度数组,刚开始我写的是就求出后缀数组sa,然后从排名第1开始找,比如找到了I,排名为I的从第sa[I]开始,那么如果这个值小于输入串的长度,直接输出就行,提交WA,看了看讨论,有这样的数组,aaaa那么应该输出1,而那个程序输出4,然后想到用高度数组,刚开始找到4的位置,然后找下一个排名的高度数组,如果lcp的值大于串的长度,那么更新结果,以此类推直到lcp的值不