后缀数组Da模板+注释 以及 dc3模板

后缀数组Da模板:

  1 /*
  2 后缀数组倍增法Da板子
  3 */
  4 #include <cstdlib>
  5 #include <cstring>
  6 #include <cstdio>
  7 #include <algorithm>
  8 using namespace std;
  9 const int N = 200000+9;
 10 int c[N];
 11 int rank[N], height[N];
 12 int sa[N],s[N],n;
 13 /*
 14 rank[i]数组的下标表示后缀是[i...n],它的值是这个后缀的排名
 15 sa[i]数组的下标表示这个后缀的排名,它的值是这个后缀在这个串中的起始位置。譬如sa[i]表示的后缀就是[sa[i]...n]
 16 c是辅助数组
 17 height[i]数组表示排名第i的后缀和排名第i-1后缀的最长公共前缀
 18 s数组放的是输入的数
 19 */
 20 bool pan(int *x,int i,int j,int k,int n)
 21 {
 22     int ti=i+k<n?x[i+k]:-1;
 23     int tj=j+k<n?x[j+k]:-1;
 24     return x[i]==x[j]&&ti==tj;
 25 }
 26 //下面这个代码是未优化版代码讲解
 27 //void build_SA(int m)
 28 //{
 29 //    int i,j,k;
 30 //    //清空数组,m就是一个范围。是根据你输入字符大小来决定
 31 //    for(i=1; i<=m; ++i) c[i]=0;
 32 //    //将原数组里面内容复制一份
 33 //    for(i=0; i<n; ++i) x[i]=s[i];
 34 //    //这个就是统计输入数组里面某一个字符出现次数
 35 //    for(i=0; i<n; ++i) c[x[i]]++;
 36 //    //求前缀和
 37 //    for(i=2; i<=m; ++i) c[i]+=c[i-1];
 38 //    //这行代码就是按照每一个后缀的第一个字母排序
 39 //    for(i=n-1; i>=0; i--) sa[--c[x[i]]]=i;
 40 //    //k表示关键字的长度
 41 //    for(int k=1; k<=n; k<<=1)
 42 //    {
 43 //        int num=0;
 44 //        //当k==1的时候是对每一个后缀得第二个字符排序
 45 //        for(i=n-k; i<n; ++i)//当k==1的时候很明显最后一个后缀(即[(n-1)...(n-1)])没有第二个字符
 46 //            y[num++]=i; //所以仅仅按照第二字符排序的时候他的排名必定靠前
 47 //
 48 //        //下面这个for循环就是对剩下的后缀进行排序
 49 //        for(i=0; i<n; ++i)
 50 //        {
 51 //            if(sa[i]>=k) //他的排序借助了我们对第一个字符排序后的数组
 52 //                y[num++]=sa[i]-k;  //这里为啥要减去k,因为我们要对每一个后缀得第二个字符排序,可是
 53 //            //y数组的值代表这个后缀从哪开始
 54 //        }
 55 //        for(i=1; i<=m; ++i) c[i]=0;
 56 //        for(i=0; i<=n; ++i) c[x[i]]++;
 57 //        for(i=2; i<=m; ++i) c[i]+=c[i-1]; //这三行和上面一样,因为在排序过程中c数组的值改变了,所以我们还要重新弄一遍
 58 //        //下面一行代码就将每一个后缀得第一个字符和第二个字符综合起来给每一个后缀排序
 59 //        for(i=n-1; i>=0; --i) sa[--c[x[y[i]]]]=y[i],y[i]=0;
 60 //        /*
 61 //        上面一行代码的作用就是用结合两个关键字把总的排序搞出来
 62 //        我们应该做的,就是先根据第一关键字排序,第一关键字相等时根据第二关键字大小排序。
 63 //        但是看上去,只进行了一次计数排序啊。
 64 //        还记得这个计数排序的特点:先根据x的值排序,x值相等时根据出现先后次序排序。
 65 //        x里面存了上次关键字的排序,在本次排序中即是第一关键字的排序,x的值排序==第一关键字排序,这里的计数排序做的是对的。那么第二关键字呢?
 66 //        前面对第二关键字进行了排序,在这里x[y[i]]就是根据第二关键字的顺序重新改变了第一关键字的顺序,也就是说在本次计数排序中,出现先后次序排序==第二关键字大小排序。
 67 //        换句话说,我们先单独对第二关键字排序,根据这个顺序改变第一关键字的顺序,由于在计数排序时首先按照第一关键字的值排序,而第一关键字的值没有改变所以首先还是根据第一关键字排序,
 68 //        改变的是第一关键字相同的时候,出现在前面的第二关键字排在前面。
 69 //
 70 //        y[i]的值就是排名第i的后缀是【y[i]...(n-1)】,那么x[y[i]]就代表了y[i]这个位置上的字符,这就做到了
 71 //        “先根据第一关键字排序,第一关键字相等时根据第二关键字大小排序。”
 72 //        */
 73 //        /*
 74 //        做到这里就完成了第一第二关键字的合并,得到了合并以后的关键字顺序,它可以用于下次迭代。
 75 //        对于下面代码每次新的迭代要用到rank数组x,由于有了刚求的关键字排序数组sa,要得到rank数组也很容易。
 76 //        但是对于相同的值,rank应该相同,所以要判断一下合并以后的关键字是否相同。
 77 //        */
 78 //        swap(x,y);
 79 //        num=1;
 80 //        x[sa[0]]=1;
 81 //        for(i=1; i<n; ++i)
 82 //        {
 83 //            if(y[sa[i]]!=y[sa[i-1]] || y[sa[i]+k]!=y[sa[i-1]+k])
 84 //                x[sa[i]]=++num;
 85 //            else x[sa[i]]=num;
 86 //        }
 87 //        if(num>=n) break;
 88 //        m=num;
 89 //    }
 90 //}
 91 void build_SA(int n,int r)
 92 {
 93     int *x=rank,*y=height;
 94     //先清空辅助数组
 95     for(int i=0; i<r; i++)c[i]=0;
 96     //因为后面要排序,所以先统计一下各数字个数
 97     for(int i=0; i<n; i++)c[s[i]]++;
 98     //求前缀和,可能你不知道为啥要求前缀和。这个一言两语说不清。建议模拟一下
 99     for(int i=1; i<r; i++)c[i]+=c[i-1];
100     //前面辅助工作做完后,下面这一行代码就对每一个后缀按第一个关键字排序了(模拟一下)
101     //第一关键字?比如后缀是abbcd那么a就是第一关键字,c就是第四关键字,d就是第五关键字,二三关键字都是b
102     for(int i=n-1; i>=0; i--)sa[--c[s[i]]]=i;
103
104     r=1;
105     x[sa[0]]=0;
106     for(int i=1; i<n; i++)  //这个x数组就是整理一下对每一个后缀第一个关键字排序的结果
107     //怎么整理?就是如果两个后缀第一关键字相同,那么排名也一样,否则就不一样,排名是从0到n
108         x[sa[i]]=s[sa[i]]==s[sa[i-1]]?r-1:r++;
109
110     for(int k=1; r<n; k<<=1)  //这个里面内容和未优化代码中的差不多
111     {
112         int yn=0;
113         //y数组里面保存着对每一个后缀的第二个关键字排序的结果
114         //注意这个时候仅仅是按照第二个关键字排序,第一个关键字没有影响这个排序
115         for(int i=n-k; i<n; i++)y[yn++]=i;
116         for(int i=0; i<n; i++)
117             if(sa[i]>=k)y[yn++]=sa[i]-k;
118         //这一部分是对每一个后缀按照第一二关键字同时排序
119         //这样的话就是先判断第一关键字相同不,相同的话再按照第二关键字排序
120         for(int i=0; i<r; i++)c[i]=0;
121         for(int i=0; i<n; i++)++c[x[y[i]]];
122         for(int i=1; i<r; i++)c[i]+=c[i-1];
123         for(int i=n-1; i>=0; i--)sa[--c[x[y[i]]]]=y[i];
124         swap(x,y);
125         r=1;
126         x[sa[0]]=0;
127         for(int i=1; i<n; i++)  //这个就是将第一第二关键字排序结果当作新的第一关键字,继而进行倍增
128             x[sa[i]]=pan(y,sa[i],sa[i-1],k,n)?r-1:r++;
129     }
130     for(int i=0; i<n; i++)rank[i]=x[i];
131 }
132 //height[i]数组表示排名第i的后缀和排名第i-1后缀的最长公共前缀
133 /*
134 能够线性计算height[]的值的关键在于h[](height[rank[]])的性质,即h[i]>=h[i-1]-1,下面具体分析一下这个不等式的由来。
135 我们先把要证什么放在这:对于第i个后缀,设j=sa[rank[i] – 1],也就是说j是i的按排名来的上一个字符串,按定义来i和j的最
136 长公共前缀就是height[rank[i]],我们现在就是想知道height[rank[i]]至少是多少,而我们要证明的就是至少是
137 height[rank[i-1]]-1。好啦,现在开始证吧。
138
139 首先我们不妨设第i-1个字符串(这里以及后面指的“第?个字符串”不是按字典序排名来的,是按照首字符在字符串中的位置来的)
140 按字典序排名来的前面的那个字符串是第k个字符串,注意k不一定是i-2,因为第k个字符串是按字典序排名来的i-1前面那个,
141 并不是指在原字符串中位置在i-1前面的那个第i-2个字符串。
142 这时,依据height[]的定义,第k个字符串和第i-1个字符串的公共前缀自然是height[rank[i-1]],现在先讨论一下第k+1个字符串
143 和第i个字符串的关系。
144 第一种情况,第k个字符串和第i-1个字符串的首字符不同,那么第k+1个字符串的排名既可能在i的前面,也可能在i的后面,但没有
145 关系,因为height[rank[i-1]]就是0了呀,那么无论height[rank[i]]是多少都会有height[rank[i]]>=height[rank[i-1]]-1,也
146 就是h[i]>=h[i-1]-1。
147 第二种情况,第k个字符串和第i-1个字符串的首字符相同,那么由于第k+1个字符串就是第k个字符串去掉首字符得到的,第i个字
148 符串也是第i-1个字符串去掉首字符得到的,那么显然第k+1个字符串要排在第i个字符串前面,要么就产生矛盾了。同时,第k个
149 字符串和第i-1个字符串的最长公共前缀是height[rank[i-1]],那么自然第k+1个字符串和第i个字符串的最长公共前缀就是
150 height[rank[i-1]]-1。
151 到此为止,第二种情况的证明还没有完,我们可以试想一下,对于比第i个字符串的字典序排名更靠前的那些字符串,谁和
152 第i个字符串的相似度最高(这里说的相似度是指最长公共前缀的长度)?显然是排名紧邻第i个字符串的那个字符串了呀,
153 即sa[rank[i]-1]。也就是说sa[rank[i]]和sa[rank[i]-1]的最长公共前缀至少是height[rank[i-1]]-1,那么就有
154 height[rank[i]]>=height[rank[i-1]]-1,也即h[i]>=h[i-1]-1。
155 证明完这些之后,下面的代码也就比较容易看懂了。
156
157 我们说了这么多就是为了证明height[i]>=height[i-1]-1,证明了这个我们就不用一个后缀和好多后缀比较。可以直接从
158 height[i-1]-1来比较
159 */
160 void get_height(int n)
161 {
162     int i,j,k=0;
163     for(i=1; i<=n; i++)rank[sa[i]]=i;
164     for(i=0; i<n; i++)
165     {
166         if(k)k--;
167         else k=0;
168         j=sa[rank[i]-1];
169         while(s[i+k]==s[j+k])k++;
170         height[rank[i]]=k;
171     }
172 }
173 int main()
174 {
175     scanf("%d",&n);
176     for(int i=0; i<n; i++)
177         scanf("%d",&s[i]),s[i]++;
178     s[n]=0;
179     build_SA(n+1,N);
180     get_height(n);
181     /*
182     注意,用这个板子的时候都要在输入的最后新加一个长度(必须加,要不然会跑不出来结果),然后像这一个板子尽量新加的
183     那个数比所有输入的数都要小。
184     这样的话排名第0的后缀肯定是[n...n]
185
186     然后主串的排名是1到n,它的height数组范围就是2到n了(这里的范围都是闭区间)
187     */
188     for(int i=2;i<=n;++i)
189     {
190         printf("%d ",height[i]);
191     }
192     printf("\n");
193     return 0;
194 }

学习后缀数组链接:

https://www.cnblogs.com/nietzsche-oier/articles/6621881.html

https://blog.csdn.net/qq_37774171/article/details/81776029

后缀数组dc3模板:

//后缀数组dc3模板
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 3000010;
#define F(x) ((x) / 3 + ((x) % 3 == 1 ? 0 : tb))
#define G(x) ((x) < tb ? (x) * 3 + 1 : ((x) - tb) * 3 + 2)
int wa[maxn], wb[maxn], Ws[maxn], wv[maxn], sa[maxn];
int Rank[maxn], height[maxn],r[maxn];
char s[maxn];
int c0(int *r, int a, int b)
{
    return r[a] == r[b] && r[a + 1] == r[b + 1] && r[a + 2] == r[b + 2];
}
int c12(int k, int *r, int a, int b)
{
    if (k == 2)
        return r[a] < r[b] || r[a] == r[b] && c12(1, r, a + 1, b + 1);
    return r[a] < r[b] || r[a] == r[b] && wv[a + 1] < wv[b + 1];
}
void Rsort(int *r, int *a, int *b, int n, int m)
{
    for (int i = 0; i < n; i++) wv[i] = r[a[i]];
    for (int i = 0; i < m; i++) Ws[i] = 0;
    for (int i = 0; i < n; i++) Ws[wv[i]]++;
    for (int i = 1; i < m; i++) Ws[i] += Ws[i - 1];
    for (int i = n - 1; i >= 0; i--) b[--Ws[wv[i]]] = a[i];
}
void dc3(int  *r,int *sa,int n, int m)
{
    int i, j, *rn = r + n, *san = sa + n, ta = 0, tb = (n + 1) / 3, tbc = 0, p;
    r[n] = r[n + 1] = 0;
    for (i = 0; i < n; i++) if (i % 3 != 0) wa[tbc++] = i;
    Rsort(r + 2, wa, wb, tbc, m);
    Rsort(r + 1, wb, wa, tbc, m);
    Rsort(r, wa, wb, tbc, m);
    for (p = 1, rn[F(wb[0])] = 0, i = 1; i < tbc; i++)
        rn[F(wb[i])] = c0(r, wb[i - 1], wb[i]) ? p - 1 : p++;
    if (p < tbc) dc3(rn, san, tbc, p);
    else for (i = 0; i < tbc; i++) san[rn[i]] = i;
    for (i = 0; i < tbc; i++) if (san[i] < tb) wb[ta++] = san[i] * 3;
    if (n % 3 == 1) wb[ta++] = n - 1;
    Rsort(r, wb, wa, ta, m);
    for (i = 0; i < tbc; i++) wv[wb[i] = G(san[i])] = i;
    for (i = 0, j = 0, p = 0; i < ta && j < tbc; p++)
        sa[p] = c12(wb[j] % 3, r, wa[i], wb[j]) ? wa[i++] : wb[j++];
    for (; i < ta; p++) sa[p] = wa[i++];
    for (; j < tbc; p++) sa[p] = wb[j++];
}
void get_height(int n)
{
    int i, j, k = 0;
    for (i = 1; i <= n; i++) Rank[sa[i]] = i;
    for (i = 0; i < n; height[Rank[i++]] = k)
        for (k ? k-- : 0, j = sa[Rank[i] - 1]; r[i + k] == r[j + k]; k++);
}
int main()
{
    while(~scanf("%s",s))
    {
        int n=strlen(s);
        if(n==1 && s[0]==‘.‘) break;
        for(int i=0;i<n;++i)
            r[i]=s[i];
        r[n]=‘0‘;
        /*
        这个用法和后缀数组Da模板一样,后面要加一个小于输入所有字符的字符
        */
        dc3(r,sa,n+1,200);
        get_height(n);
        for(int i=2;i<=n;++i)
            printf("%d ",height[i]);
        printf("\n");
    }
    return 0;
}

dc3模板学习链接:

https://www.cnblogs.com/jianglangcaijin/p/6035937.html

原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/12321985.html

时间: 2024-11-03 07:18:50

后缀数组Da模板+注释 以及 dc3模板的相关文章

后缀数组 - 求最长公共子序列 + 模板题 : poj 2774

// Memory Time // 1347K 0MS // by : Snarl_jsb // 2014-09-24-19.57 #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<vector> #include<queue> #include<stack>

UOJ35 后缀数组(模板)

#35. 后缀排序 这是一道模板题. 读入一个长度为 nn 的由小写英文字母组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置.位置编号为 11 到 nn. 除此之外为了进一步证明你确实有给后缀排序的超能力,请另外输出 n−1n−1 个整数分别表示排序后相邻后缀的最长公共前缀的长度. 输入格式 一行一个长度为 nn 的仅包含小写英文字母的字符串. 输出格式 第一行 nn 个整数,第 ii 个整数表示排名为 ii 的后缀的第一个字符在原串中

Power Strings POJ - 2406 后缀数组

Given two strings a and b we define a*b to be their concatenation. For example, if a = "abc" and b = "def" then a*b = "abcdef". If we think of concatenation as multiplication, exponentiation by a non-negative integer is defin

poj 1743 后缀数组 求最长不重叠重复子串

题意:有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题. “主题”是整个音符序列的一个子串,它需要满足如下条件:1.长度至少为5个音符2.在乐曲中重复出现(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值.)3.重复出现的同一主题不能有公共部分. 链接:点我先转化成相邻两项的差值,然后就是找不可重叠重复子串.做法就是二分答案LEN然后根据height值进行分组 第一道后缀数组题,测了一下模板

后缀数组模板及解释

以前做过后缀数组,直接用模板,最近打算重新认真的学一遍.感觉学一个东西一定要弄懂了,不然到最后还是要重学. int wa[MAXN],wb[MAXN],wv[MAXN],Ws[MAXN]; void da(int *r,int *sa,int n,int m){//n表示字符串长度 + 1,包括添加的那个0,m表示取值的范围 //把单个字符进行基数排序 int *x = wa,*y = wb; for(int i = 0; i < m; i++)Ws[i] = 0; for(int i = 0;

后缀数组模板第一版

/*---------------倍增算法+RMQ后缀数组模板-------------- 输入:从0开始的字符串g,长度len最大为10^6 输出: sa[]表示:n 个后缀从小到大进行排序之后把排好序的后缀的开头位置顺 次放入 sa 中,sa[i]表示排第i位的字符串开头是sa[i],因为添加了一个结尾0,所以sa[0]=len height 数组(h[]):定义 h[i]=suffix(sa[i-1])和 suffix(sa[i])的最长公 共前缀,也就是排名相邻的两个后缀的最长公共前缀.

[模板] 后缀数组 C++

以下模板单单注释了如何使用,算法详解可参考 罗穗骞<后缀数组——处理字符串的有力工具> 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 5 using namespace std; 6 7 const int maxn = //长度范围 8 const int maxm = //字符范围

PKU 2774 Long Long Message (后缀数组练习模板题)

题意:给你两个字符串,求最长公共字串的长度. by:罗穗骞模板 #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define M 303 #define inf 0x3fffffff #define maxn 500000 #define ws ww #define rank RANK #define F

后缀数组(Suffix Array)模板及简析——Part 1:构建SA和rank数组

后缀数组(Suffix Array,SA)是处理字符串的有力工具.它比后缀树更易实现,占用空间更少,并且同样可以解决千变万化的字符串问题 首先推荐罗穗骞的论文(网上搜一下就能搜到),里面对后缀数组的定义.实现和应用都做了详细的阐述 然而不幸的是罗神犇的代码简直魔性,蒟蒻表示这代码压的根本看不懂啊…… 所以在理解了后缀数组的构建过程之后,我重新写了一份模板代码.虽然啰嗦了点(代码比较大,而且变量名故意拉长了),不过相对比较好懂 而且论文中用到的辅助空间是4N,我的模板用了3N,事实上还可以优化到只