|算法讨论|后缀数组 学习笔记
来源:互联网 发布:奶牛场优化养殖 编辑:程序博客网 时间:2024/06/06 04:08
模板及讲解
解决字符串的有力工具。
直接上代码,注释讲解(此题为uoj #35)
#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#define ms(i, j) memset(i, j, sizeof i)#define FN2 "uoj35" using namespace std;const int MAXN = 1000000 + 5;char ch[MAXN];int a[MAXN], n, m;//ch为输入字符串,a为处理后的整数原串 int SA[MAXN], rk[MAXN], tp[MAXN], tax[MAXN], height[MAXN];/* SA[i]: 排名第i的后缀的位置(下标是后缀排名) rk[i]: 第i个后缀的排名 tp[i]: 排名为i的第二关键字的第一关键字的位置 (下标是第二关键字排名) tax[i]:基数排序的桶 height[i]: 排名为i, i-1后缀的最长公共前缀 */bool cmp(int *f, int i, int k) {return f[SA[i-1]]==f[SA[i]]&&f[SA[i-1]+k]==f[SA[i]+k];} //要判断两个字符串是否完全相等 void build() {//构造后缀数组 int i, p; for (int i=0;i<m;i++) tax[i] = 0; for (int i=0;i<n;i++) tax[rk[i]=a[i]]++; for (int i=0;i<m;i++) tax[i] += tax[i-1]; for (int i=n-1;i>=0;i--) SA[--tax[rk[i]]] = i;//基数排序排第一轮 for (int k=1;k<=n;k*=2) { p = 0; for (int i=n-k;i<n;i++) tp[p++] = i;//(n-k)~(n-1)无第二关键字,所以排序应该排在前面 for (int i=0;i<n;i++) if (SA[i]>=k) tp[p++] = SA[i] - k; //只有SA[i]>=k的SA[i]才是第二关键字的位置 //从图中可以看出第一关键字和第二关键字的位置相差k,故SA[i] - k for (int i=0;i<m;i++) tax[i] = 0; for (int i=0;i<n;i++) tax[rk[tp[i]]]++;//x[tp[i]]相等于排名第i的第二关键字的第一关键字的排名 for (int i=1;i<m;i++) tax[i] += tax[i-1]; for (int i=n-1;i>=0;i--) SA[--tax[rk[tp[i]]]] = tp[i];//保证了第一关键字的顺序再排第二关键字 //基数排序第一关键字(rank[i]的数值)和第二关键字(tp[i]的下标) swap(rk, tp);//此时tp没用,暂存上一轮rank的值 p = 0, rk[SA[0]] = 0;//sa[0]一定是添加的字符0, 排名一定是0 for (int i=1;i<n;i++) rk[SA[i]] = cmp(tp, i, k) ? p : ++p; //算排名第i的数的rank,按sa顺序能够保证rank的正确性,但是要cmp判断与上一个字符串相等的情况 if (++p>=n) break;//剪枝,已经没有重复元素 m = p; }}void getH() {//算height int i, j, k = 0;//k是比i-1前一名的后缀 for (int i=0;i<n;i++) {//H[0], H[1], H[2] ...的顺序计算 if (k) k--;//从k-1开始比较 ,运用结论H[i]>=H[i-1]-1, 最长公共前缀的长度至少是k-1(k = H[i-1]) j = SA[rk[i]-1]; //前一名的后缀位置 while(ch[i+k] == ch[j+k]) k++; //往后比较 height[rk[i]] = k; //更新答案 }}void init() { int i; n = strlen(ch) + 1, m = 128; for (int i=0;i<n-1;i++) a[i] = ch[i];//处理原字符串 a[n - 1] = 0;//补末尾的0 }void solve() { int i; build(); getH(); for (int i=1;i<n;i++) printf("%d ", SA[i]+1);//输出范围1~n-1 printf("\n"); for (int i=2;i<n;i++) printf("%d ", height[i]);}int main() { scanf("%s", ch), init(), solve(); return 0;}
常见题型:
1、直接运用后缀数组
Q:给出一个字符串,询问该字符串的后缀排名/排第i为后缀位置/相邻两个后缀最长公共前缀。
解:运用后缀数组处理的
例题: uoj #35
2、求不同子串的个数
Q:给出一个字符串,求出该字符串中不相同子串的总个数。
解:转化为求不同前缀个数问题,每个
例题:spoj 694
3、划分翻转字典序最小问题(多次求后缀数组)
Q:给出一个数组,把数组分成3份,每一份都要翻转,输出字典序最小的结果
解:运用后缀数组的字典序排序分解问题,多次求后缀数组
例题:poj3581
4、求两个字符串的最长公共子串
Q:给出两个字符串,求这两个字符串的最长公共子串。
解:可以连接两个字符串,中间插入
例题:poj 2774
5、求两个字符串长度不小于k的公共子串的个数(分组)
Q:给出两个字符串,求这两个字符串长度不小于k的公共子串的个数。
解:
例题:poj3415
6、求多个字符串的公共子串(二分+分组)
Q:给出多个字符串,求这几个字符串的公共子串。
解:用不同的符号连接每个字符串,然后二分公共子串的长度,在
例题:Hdu 2328
阅读全文
0 0
- |算法讨论|后缀数组 学习笔记
- |算法讨论|树状数组 学习笔记
- 后缀数组--学习笔记(倍增算法)
- 后缀数组 学习笔记
- 后缀数组学习笔记
- 学习笔记----后缀数组
- 后缀数组学习笔记
- 后缀数组学习笔记
- 后缀数组学习笔记
- 后缀数组学习笔记
- 后缀数组学习笔记
- 后缀数组学习笔记
- 后缀数组 学习笔记
- 后缀数组学习笔记
- 后缀数组学习笔记——罗穗骞倍增算法代码
- 后缀数组学习笔记——罗穗骞倍增算法代码
- 后缀数组学习笔记——罗穗骞倍增算法代码
- 后缀数组学习笔记——罗穗骞倍增算法代码
- JDK源码【集合框架】list
- LeetCode前3题
- 如何解决idea中XML配置文件里面的URI is not registered问题
- JqGrid 修改为application/json模式
- 使用 Linux 的 strace 命令跟踪/调试程序的常用选项
- |算法讨论|后缀数组 学习笔记
- linux centos基本命令
- 在Fragment中修改Activity中的控件
- 较好的程序设计竞赛有哪些
- 数据结构实验之栈七:出栈序列判定
- ORA-12154: TNS: 无法解析指定的连接标识符 plsql
- HD-2842 Chinese Rings(矩阵应用)
- POJ 2955
- 模板类与类模板、函数模板与模板函数等的区别