后缀数组

匿名 (未验证) 提交于 2019-12-02 23:52:01

后缀数组

给出一个长度为n的字符串\(\{s_i\}\),定义它的后缀为一个i,表示子串\(s[i\sim n]\),现在请维护出\(SA[i]\)为存储后缀的数组,但是按照其对应的子串的字典序排序,\(Height[i]\)为第i个后缀和第i+1个后缀对应的子串公共前缀的长度,\(n\leq 3\times 10^5\)

最直接的思路是对后缀数组直接排序,问题在于时间复杂度为\(O(n^2log(n))\),我们可以尝试优化其中的\(O(n)\)的比较,因为子串的比较,维护出前缀hash值,以后,可以通过二分找到第一个前缀不相等的位置,注意不存在这个位置需要特判,然后向后比较一个,就能知道两个子串的大小了,这样比较就优化到了\(log(n)\)

而至于最长公共前缀同样的方法二分,于是可以做到时间复杂度\(nlog(n)^2\)

参考代码:

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define il inline #define ri register #define ll long long #define ull unsigned ll #define Size 300500 #define jzm 19260817 using namespace std; char s[Size]; int sl,suf[Size]; ull base[Size],hs[Size]; il void prepare(); il bool comp(const int,const int); il int ask(int,int),dfs(int,int); template<class free>il free Min(free,free); int main(){     scanf("%s",s+1);     sl=strlen(s+1),prepare();     for(int i(1);i<=sl;++i)suf[i]=i;     sort(suf+1,suf+sl+1,comp);     for(int i(1);i<=sl;++i)         printf("%d ",suf[i]-1);     putchar('\n'),putchar(48);     for(int i(2);i<=sl;++i)         printf(" %d",dfs(suf[i],suf[i-1]));     return 0; } il bool comp(const int x,const int y){     int p(dfs(x,y));     if(x+p-1>=sl||y+p-1>=sl)return x>y;     return s[x+p]<s[y+p]; } template<class free> il free Min(free a,free b){     return a<b?a:b; } il int dfs(int p1,int p2){     int l(1),r(Min(sl-p1+1,sl-p2+1)),mid;     while(l<=r){mid=l+r>>1;         if(ask(p1,p1+mid-1)==ask(p2,p2+mid-1))l=mid+1;         else r=mid-1;     }return r; } il int ask(int l,int r){     return hs[r]-hs[l-1]*base[r-l+1]; } il void prepare(){base[0]=1;     for(int i(1);i<=sl;++i)         base[i]=base[i-1]*jzm,             hs[i]=hs[i-1]*jzm+s[i]; }
转载请标明出处:后缀数组
文章来源: 后缀数组
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!