后缀数组-倍增法

额,写的有点混乱,改天整理一下

链接:https://hihocoder.com/problemset/problem/1403

 1 //字符串从1开始
 2 //rank[i]为字符串中i位置起的后缀排序后的顺序
 3 //sa[i]为排序后的第i位置对应的后缀在字符串中的起始位置
 4 //sa[rank[i]]=i此公式在最终结果才成立
 5 //height[i]为排序数组中i位置与i-1位置的最长公共前缀的长度
 6 //H[i]为字符串i位置起的后缀与其在排序后前面相邻位置处的后缀的最大公共前缀
 7 //H[i]=height[rank[i]]此公式在最终结果时才成立(因为过程中rank[i]可能会与rank[j]重复)
 8 void solve()
 9 {
10     for(int i=0;i<256;i++) cntA[i]=0;
11     for(int i=1;i<=n;i++) cntA[ch[i]]++;
12     //字符产从1开始,统计每个字符出现个数
13     for(int i=1;i<256;i++) cntA[i]+=cntA[i-1];
14     //统计该字符前出现字符的个数
15     for(int i=n;i;i--) sa[cntA[ch[i]]--] = i;
16     //至此单个字符排序的sa数组构成
17     rank[sa[1] ] = 1;
18     //根据sa数组顺序,开始求rank数组
19     for(int i=2;i<=n;i++)
20     {
21         rank[sa[i]] = rank[sa[i-1]];
22         //sa相邻字符相同时rank值相同
23         if(ch[sa[i]]!=ch[sa[i-1]])
24             rank[sa[i]]++;
25         //相邻字符不相同时rank值增加1
26     }
27     //后续排序的对象是之前初次排序的次序值,并非真实值
28     for(int l=1;rank[sa[n]]<n;l<<=1)//l为已有中间结果的长度
29     {//结束条件:应该可以换为l<n,当l>=n时结束,求出的是各后缀数组的rank和sa
30      //采用当前这种形式,是考虑到l不必扩展到n即可得出最终结果的情况
31      //rank[sa[n]]<n表明存在rank[i]==rank[j]的情况,此时还需继续计算
32      //最终的结果中是不会出现重复rank的情况
33      //而rank[sa[n]]>=n,则表示不存在重复rank,即可代表后缀排序,此时已经可以作为最终结果
34         for(int i=0;i<=n;i++) cntA[i] = 0;
35         for(int i=0;i<=n;i++) cntB[i] = 0;
36         for(int i=1;i<=n;i++)
37         {
38             //到n截止是因为字符串长n
39             //排序后最后序值不会过n
40             cntA[A[i] = rank[i]]++;
41             //A数组记录i位置处第一关键字值即排序
42             //cntA数组记录各第一关键字出现次数
43             cntB[B[i]=(i+l<=n)?rank[i+l]:0 ]++;
44             //B数组记录i位置处第二关键字值即在整体中的排序
45             //cntB数组记录各第二关键字出现次数
46         }
47         for(int i=1;i<=n;i++) cntB[i]+=cntB[i-1];
48         //计算i位置处当前第二关键字及之前的个数
49         for(int i=n;i;i--)
50             tsa[cntB[B[i]]--]=i;
51         //tsa数组作临时sa
52         //记录按第二关键字排序的sa
53         for(int i=1;i<=n;i++) cntA[i]+=cntA[i-1];
54         //计算当前第一关键字及之前的个数
55         for(int i=n;i;i--)
56             sa[cntA[A[tsa[i]]]--] = tsa[i];
57         //此时求得的sa是最终的sa数组,综合了第一、二关键字
58         //tsa从大到小进行安排,即从第二关键字排序靠后的开始分配
59         //找到第二关键字排序最靠后的位置即tsa[i]
60         //然后找到其第一关键字的排序A[tsa[i]]
61         //更新其sa,
62         //由于其第二关键字排序最靠后,
63         //所以即使当前第一关键字顺序处有多个,
64         //也为其在sa数组中相同第一关键字所在范围内分配最后一个位置,即,使其排序靠后
65         rank[sa[1]] = 1;
66         //原理同上,计算rank数组
67         for(int i=2;i<=n;i++)
68         {
69             rank[sa[i]] = rank[sa[i-1]];
70             if(A[sa[i]]!=A[sa[i-1]]||B[sa[i]]!=B[sa[i-1]])
71                 rank[sa[i]]++;
72             //因为当前是双关键字
73         }
74     }
75     //此时求出了rank和sa数组
76     //下面开始求height数组
77     //感觉更像是求H数组
78     //H[i] = height[rank[i]]
79     for(int i=1;j=0;i<=n;i++)
80     {
81         //i表示在字符串中的位置,从1到n
82         if(j) j--;
83         //j为H[i-1]的值,即上次计算结果
84         //利用了H[i]>=H[i-1]-1的性质
85         //所以可以在此基础上计算
86         while(ch[i+j] == ch[sa[rank[i]-1]+j]) j++;
87         //sa[rank[i]-1]表示sa数组中前一个的后缀序号
88         //累加计算最大公共前缀
89         height[rank[i]] = j;
90     }
91 }

时间: 2024-08-26 17:05:55

后缀数组-倍增法的相关文章

后缀数组倍增法理解

int wa[maxn],wb[maxn],wv[maxn],ws[maxn]; int cmp(int *r , int a, int b, int l) { return r[a] == r[b] && r[a+l] == r[b+l]; } void da (int *r , int *sa , int n, int m) { int i, j, p, *x = wa, *y = wb , *t; for(i = 0; i < m; i++) ws[i] = 0; for(i

后缀数组-倍增算法模板

void build_sa(int m) { int *x = t, *y = t2; for(int i = 0; i < m; i ++) c[i] = 0; for(int i = 0; i < n; i ++) c[x[i]=s[i]] ++; for(int i = 1; i < m; i ++) c[i] += c[i-1]; for(int i = n-1; i >= 0; i --) sa[--c[x[i]]] = i; for(int k = 1; k <=

后缀数组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

后缀数组之倍增算法

首先说明 :后缀数组的构建在网上有多种方法:朴素的n*n*logn,还有倍增n*logn的,还有3*n的DC3算法,当然还有DC算法.这个算法学习自林厚丛老师的<高级数据结构>,代码较长,而且常数也比较大,但是是我这种笨人可以理解的.如有人想学短而快的可以学习<罗穗骞 后缀数组 ---处理字符串的有力工具>.顺便说一下,罗大神的算法书写的的确很短小也漂亮,可惜我看不懂. 说一下学习的心路历程吧!最开始想学后缀树,道理看明的了,可是一看代码实在是太长了(可能是我找的模版不对吧).后来

后缀数组(理解)

一.基本概念   后缀:用  suff[i]  表示,是指从某个位置 i 开始到整个串末尾结束的一个子串. 后缀数组:用 sa[i] 表示,是指所有后缀在排完序后,排名为i的后缀在原串中的位置.   sa[排名]=位置 名次数组:用 rank[i] 表示,是指所有后缀在排序完后,原字符串中第i个后缀现在的排名.   rank[位置]=排名 比如字符串aabaaaab$,(我们习惯在字符串后面加一个特殊字符$,表示字符串的结尾)他的所有后缀.位置.排名如下: 后缀 位置 排名 suff[ 1 ]

hdu 3518 Boring counting 后缀数组LCP

题目链接 题意:给定长度为n(n <= 1000)的只含小写字母的字符串,问字符串子串不重叠出现最少两次的不同子串个数; input: aaaa ababcabb aaaaaa # output 2 3 3 思路:套用后缀数组求解出sa数组和height数组,之后枚举后缀的公共前缀长度i,由于不能重叠,所以计数的是相邻height不满足LCP >= i的. 写写对后缀数组倍增算法的理解: 1.如果要sa数组对应的值也是1~n就需要在最后加上一个最小的且不出现的字符'#',里面y[]是利用sa数

bnuoj 34990(后缀数组 或 hash+二分)

后缀数组倍增算法超时,听说用3DC可以勉强过,不愿写了,直接用hash+二分求出log(n)的时间查询两个字符串之间的任意两个位置的最长前缀. 我自己在想hash的时候一直在考虑hash成数值时MOD取多大,如果取10^18的话,那么两数相乘个就超LL了,但是取10^9的话又怕出现重复的可能大.后面才发现自己是sb,如果用unsigned long long 如果有溢出或者为负数是直接变成对(1<<64)取模了. 也就是无符号长整形运算自动帮你取模了.所以可以放心用hash Justice S

用倍增法构造后缀数组中的SA及RANK数组

感觉后缀数组很难学的说= = 不过总算是啃下来了 首先 我们需要理解一下倍增法构造的原理 设原串的长度为n 对于每个子串 我们将它用'\0'补成长度为2^k的串(2^k-1<n<=2^k) 比如串aba的子串就有 aba'\0'    ba'\0''\0'  a'\0''\0''\0' 每次操作我们可以排出所有长度为 2^x的子串的大小 比如串aba的排序过程 第一遍 a                   a             b 第二遍 a'\0'             ab  

后缀数组 DC3构造法 —— 详解

学习了后缀数组,顺便把DC3算法也看了一下,传说中可以O(n)复杂度求出文本串的height,先比较一下倍增算法和DC3算法好辣. DC3 倍增法 时间复杂度 O(n)(但是常数很大)   O(nlogn)(常数较小) 空间复杂度   O(n)    O(n) 编程复杂度    较高   较低 由于在时间复杂度上DC3的常数比较大,再加上编程复杂度比较高,所以在解决问题的时候并不是最优选择.但是学到了后缀数组还是补充一下的好点. DC3算法的实现: 1:先把文本串的后缀串分成两部分,第一部分是后