后缀数组模板(全注释)
2017-12-16 07:41
197 查看
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> #define enter putchar('\n') #define space putchar(' ') using namespace std; typedef long long ll; template <class T> void read(T &x){ char c; bool op = 0; while(c = getchar(), c < '0' || c > '9') if(c == '-') op = 1; x = c - '0'; while(c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; if(op) x = -x; } template <class T> void write(T x){ if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar('0' + x % 10); } const int N = 100005; int n; char s ; int buf1 , buf2 , buc , sa , rnk , height ; void suffix_sort(){ int *x = buf1, *y = buf2, m = 127; //m是基数排序的基数的最大值 //buf1和buf2是两个用来缓存的临时数组,用指针定义方便循环使用 /**** 接下来先对所有单个字符基数排序 ****/ for(int i = 0; i <= m; i++) buc[i] = 0; //先把桶清空 for(int i = 1; i <= n; i++) buc[x[i] = s[i]]++; //此时,x[i]是一个字符串的“值” for(int i = 1; i <= m; i++) buc[i] += buc[i - 1]; for(int i = n; i; i--) sa[buc[x[i]]--] = i; for(int k = 1; k <= n; k <<= 1){ //倍增枚举所比较的字符串的长度(字符串的长度的一半是k,即x[i]中此时存储的信息来自i开头长为k的字符串) int p = 0; //p统计当前放入新桶的字符串的个数 //由于上一轮排序后,sa数组中存储的顺序相当于这一轮的第二关键字排序结果 //所以接下来把上一轮的排序结果转译成这一轮字符串的按第二关键字排序结果,把排好序的后缀放在y数组中 for(int i = n - k + 1; i <= n; i++) y[++p] = i; //对于所有右半部分为空的字符串,它们的第二关键字最小,所以排在最前面 for(int i = 1; i <= n; i++) if(sa[i] > k) y[++p] = sa[i] - k; //按顺序枚举上一轮排序后的sa数组中的字符串,如果它作为第二关键字对某个字符串有贡献 //则将被贡献字符串放入y数组 //至此,y数组中是1~n的后缀编号,已按第二关键字大小升序排列。 for(int i = 0; i <= m; i++) buc[i] = 0; //再进行一次基数排序 for(int i = 1; i <= n; i++) buc[x[y[i]]]++; //还记得x[i]存的是什么吗?是i开头长为k的字符串的“值”,即第一关键字 for(int i = 1; i <= m; i++) buc[i] += buc[i - 1]; for(int i = n; i; i--) sa[buc[x[y[i]]]--] = y[i]; /**** 接下来更新一个字符串的“值”——这次是长为2k的字符串的“值”了 ****/ swap(x, y), x[sa[1]] = p = 1; //注意这里交换了一下x和y……现在y是上一轮(长为k)的“值”,x是这一轮(长为2k)的“值” for(int i = 2; i <= n; i++) if(y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k]) x[sa[i]] = p; //两个字符串的两个关键字完全相同,则新的“值”也相同 else x[sa[i]] = ++p; //否则它对应着一个新的“值” if((m = p) >= n) break; //如果每个后缀对应值都不一样,则已经排好序了 } /**** 接下来记录rank数组 ****/ for(int i = 1; i <= n; i++) rnk[sa[i]] = i; /**** 接下来处理height数组:sa[i]和sa[i - 1]对应字符串的LCP ****/ for(int i = 1, j, k = 0; i <= n; i++){ if(rnk[i] == 1) continue; if(k) k--; //h[i] >= h[i - 1] - 1,k记录上一个h[i] j = sa[rnk[i] - 1]; while(s[i + k] == s[j + k] && i + k <= n && j + k <= n) k++; height[rnk[i]] = k; } } int main(){ scanf("%s", s + 1), n = strlen(s + 1); suffix_sort(); for(int i = 1; i <= n; i++) write(sa[i]), i == n ? enter: space; for(int i = 2; i <= n; i++) write(height[i]), i == n ? enter: space; return 0; }
相关文章推荐
- 后缀数组模板 (详细注释)
- Eclipse Java注释模板设置详解
- Eclipse/MyEclipse注释模板和格式化模板的使用
- AndroidStudio修改注释模板中作者和日期
- VS里的新建模板(自动添加版本注释)
- T4 模板自动生成带注释的实体类文件 - 只需要一个 SqlSugar.dll
- my中添加注释模板
- Eclipse Java注释模板设置详解
- 使用Visual Assistant X创建C程序注释模板
- QT 注释模板
- Idea 创建注释模板
- IDEA活动模板中注释的日期格式
- eclipse/myeclipse注释模板的修改
- eclipse注释模板的问题
- eclipse注释模板一个
- Android studio 中设置作者和创建日期等注释模板
- eclipse 自定义注释模板
- java开发工具一个很好的注释模板
- MyEclipse设置Java代码注释模板