BZOJ 3230 相似子串 后缀数组
来源:互联网 发布:人工智能有哪些岗位 编辑:程序博客网 时间:2024/06/06 07:17
之前觉得后缀数组谜一样的豆腐块代码很难理解,于是先去学了后缀自动机,然而这道题让我发现后缀数组还是很好用的工具QaQ。
关于这道题的每个询问首先要求出第i小和第j小的子串在原串的位置,然后快速地求出这两个子串的最长公共前缀和后缀。
第一个步骤我们可以用二分搞定,通过原串的height数组我们可以求出一个sum数组,sum[i]表示按照字典序排序后,前i个后缀中共有多少个本质不同的子串。在n个后缀中,每个子串都可以被表示为某个后缀的前缀,所以我们在sum数组中二分查找出第一个不小于k的sum[i]来求原串第k小的子串。那么第i个后缀的某个前缀就是所需的子串了,这个稍微计算一下得出。
我们得出了两个子串[la,ra]和[lb,rb],要求它们的最长公共前缀以及后缀,最长公共前缀就是height数组上的区间最小值问题了,那么后缀我们按照原串的反串建立后缀数组也就变成了height数组上的区间最小值问题了。
#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#define SZ 100005using namespace std;int n, q, s1[SZ], s2[SZ];int sa1[SZ], r1[SZ], h1[SZ], rmq1[SZ][18];int sa2[SZ], r2[SZ], h2[SZ], rmq2[SZ][18];long long sum[SZ];void Read (){ scanf ("%d %d", &n, &q); char c = getchar(); while (c < 'a' || c > 'z') c = getchar(); for (int i = 1; i <= n; i++) { s1[i-1] = s2[n-i] = c-'a'+1; c = getchar(); }}int cmp (int *s, int i, int j, int l){return s[i]==s[j] && s[i+l]==s[j+l];}void buildsa (int *s, int *sa, int *rank, int *height, int m) { n++; int a[SZ], b[SZ], c[SZ], d[SZ]; int i, j, p, k = 0, *x = a, *y = b, *t; for (i = 0; i < m; i++) c[i] = 0; for (i = 0; i < n; i++) c[x[i]=s[i]]++; for (i = 1; i < m; i++) c[i] += c[i-1]; for (i = n-1; i >= 0; i--) sa[--c[x[i]]] = i; for (j = p = 1; p < n; j<<=1, m=p) { for (p = 0, i = n-j; i < n; i++) y[p++] = i; for (i = 0; i < n; i++) if (sa[i] >= j) y[p++] = sa[i]-j; for (i = 0; i < n; i++) d[i] = x[y[i]]; for (i = 0; i < m; i++) c[i] = 0; for (i = 0; i < n; i++) c[d[i]]++; for (i = 1; i < m; i++) c[i] += c[i-1]; for (i = n-1; i >= 0; i--) sa[--c[d[i]]] = y[i]; for (t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i < n; i++) x[sa[i]] = cmp(y,sa[i-1],sa[i],j) ? p-1 : p++; } n--; 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]; s[i+k]==s[j+k]; k++);}void BuildSa (){ buildsa(s1,sa1,r1,h1,27); buildsa(s2,sa2,r2,h2,27); sum[1] = n-sa1[1]; for (int i = 2; i <= n; i++) sum[i] = sum[i-1]+(long long)(n-sa1[i]-h1[i]);}void BuildRmq (){ for (int i = 1; i <= n; i++) rmq1[i][0] = h1[i], rmq2[i][0] = h2[i]; for (int i = 1; (1<<i) <= n; i++) for (int j = 1; j+(1<<i)-1 <= n; j++) rmq1[j][i] = min(rmq1[j][i-1],rmq1[j+(1<<i-1)][i-1]), rmq2[j][i] = min(rmq2[j][i-1],rmq2[j+(1<<i-1)][i-1]);}void Query (long long k, int &lk, int &rk){ if (sum[n] < k) {lk = -1; return;} int l = 1, r = n, mid; while (l < r) { mid = (l+r)/2; if (sum[mid] >= k) r = mid; else l = mid + 1; } lk = sa1[l]; rk = sa1[l]+k+h1[l]-(l?sum[l-1]:0)-1;}int lcp (int l, int r, bool flag){ if (l == r) return 9999999; if (flag) { l = r1[l], r= r1[r]; if (l > r) swap(l,r); int k = 0; l++; if (l == r) return rmq1[l][0]; while ((1<<k+1) <= r-l+1) k++; return min(rmq1[l][k],rmq1[r-(1<<k)+1][k]); } else { l = r2[l], r= r2[r]; if (l > r) swap(l,r); int k = 0; l++; if (l == r) return rmq2[l][0]; while ((1<<k+1) <= r-l+1) k++; return min(rmq2[l][k],rmq2[r-(1<<k)+1][k]); }}void SolveQue (){ #define SKIP_THIS puts("-1");continue; long long a, b, ansa, ansb, mxans; int la, lb, ra, rb; while (q--) { scanf ("%lld %lld", &a, &b); Query (a, la, ra); Query (b, lb, rb); if (la < 0 || lb < 0) {SKIP_THIS} mxans = (long long)(min(ra-la,rb-lb) + 1); ansa = min((long long)lcp(la,lb,1), mxans); ansb = min((long long)lcp(n-ra-1,n-rb-1,0), mxans); printf ("%lld\n", ansa*ansa+ansb*ansb); }}int main (){ Read(); BuildSa(); BuildRmq(); SolveQue(); return 0;}
【蒟蒻的自述】
然而蒟蒻不理解后缀数组的豆腐块代码,所以一开始出了好多错误。
在前一部分计算sa数组的时候,为了防止出错所以在原串后加入一个原串没有的字符,并且它小于任何一个原串内的字符,所以传入的n实际是原串长度+1,并且原串s转换后最小字符不能是0,因为被加在末尾的字符是最小的。这也导致sa数组的有效位置是[1,n],而rank仍然是[0,n-1],不过我们仍然可以按照字面意思来理解它们,sa[i]表示排名第i的后缀的开始位置,rank[i]表示开始位置为i的后缀的排名,height[i]表示排名第i的后缀与排名第i-1的后缀的LCP。
0 0
- BZOJ 3230 相似子串 后缀数组
- bzoj 3230 相似子串 后缀数组
- 【BZOJ 3230】相似子串 后缀数组
- BZOJ 3230 相似子串|后缀数组|RMQ
- bzoj 3230: 相似子串 (后缀数组+RMQ+二分)
- BZOJ 3230 相似子串 后缀数组+二分+ST表
- bzoj 3230: 相似子串 后缀数组+rmq+二分
- bzoj 3230 相似子串
- [BZOJ3230]相似子串(后缀数组+二分+st表)
- bzoj3230: 相似子串(后缀数组+ST表)
- BZOJ3230 相似子串 后缀自动机做法
- BZOJ 3230 后缀数组+ST
- 后缀子串排序(后缀数组)
- 后缀数组--重复子串
- BZOJ 2946 [Poi2000]公共串 后缀数组
- 【BZOJ 2946】[Poi2000]公共串 后缀数组
- BZOJ 2946: [Poi2000]公共串 后缀数组
- 【bzoj 1692】后缀数组
- base64的编解码问题
- 如何面试架构师
- 第四周【递归函数fib】
- apache maven 基本知识
- c++作业2
- BZOJ 3230 相似子串 后缀数组
- cocos2d-x 自定义动态特效背景
- OpenJDK计划简化Java编程
- 第四周项目训练3
- FusionCharts使用
- Handler消息机制源码解析(四)
- c++实验二 两点间距离
- 据说是刷爆美国朋友圈的21幅职场哲理漫画
- 第二次上机实验-2