UVA 10829 后缀数组+技巧计数
来源:互联网 发布:中信信用卡淘宝v卡 编辑:程序博客网 时间:2024/06/05 11:11
题意:
题目链接:https://vjudge.net/problem/UVA-10829
找出一个字符串中,形如UVU的且V长度为g的子串的个数,其中U非空。
思路:
可以用后缀数组也可以直接暴力,但该题重点是在计数方式上。
后缀数组可以计算两个起点i,j的LCP,但是要保证中间要夹上正好长度为g的V,这需要枚举i,j,复杂度为O(n^2),太高了。
这里有一个技巧,考虑假设U的长度为L,那么对于字符串的{0,L,2L,3L,…}这些位置,每个长度为L的U只会覆盖其中的一个位置,假设左边的U覆盖的位置是x,那么右边的U的对应位置为x+g+L,那我们就看覆盖这两个位置的有多长的区间相同,于是对x和x+g+L分别正向和反向求出LCP0和LCP1,LCP0和LCP1的长度都不能超过L,否则会产生重复,设len = LCP0+LCP1-1,这样就得到了长度为len的相同区间。在这个区间中,可以找到len - L + 1个长度为L的,且仅仅覆盖位置x的U,更新答案即可。
代码:
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int MAXN = 1e5 + 10;int t1[MAXN], t2[MAXN], c[MAXN];bool cmp(int *r,int a,int b,int l) { return r[a] == r[b] && r[a+l] == r[b+l];}void solve(int s[], int sa[], int rk[], int height[], int n, int m) { n++; int i, j, p, *x = t1, *y = t2; //第一轮基数排序,如果s的最大值很大,可改为快速排序 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 = 1; j <= n; j <<= 1) { p = 0; //直接利用sa数组排序第二关键字 for(i = n-j; i < n; i++)y[p++] = i;//后面的j个数第二关键字为空的最小 for(i = 0; i < n; i++)if(sa[i] >= j)y[p++] = sa[i] - j; //这样数组y保存的就是按照第二关键字排序的结果 //基数排序第一关键字 for(i = 0; i < m; i++)c[i] = 0; for(i = 0; i < n; i++)c[x[y[i]]]++; for(i = 1; i < m; i++)c[i] += c[i-1]; for(i = n-1; i >= 0; i--)sa[--c[x[y[i]]]] = y[i]; //根据sa和x数组计算新的x数组 swap(x,y); p = 1; x[sa[0]] = 0; for(i = 1; i < n; i++) x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++; if(p >= n)break; m = p;//下次基数排序的最大值 } int k = 0; n--; for(i = 0; i <= n; i++)rk[sa[i]] = i; for(i = 0; i < n; i++) { if(k) k--; j = sa[rk[i]-1]; while(s[i+k] == s[j+k]) k++; height[rk[i]] = k; }}int s[MAXN];int sa[MAXN], height[MAXN], rk[MAXN], dp[MAXN][20];void RMQ_init(int n) { for (int k = 0; k < 2; k++) { for (int i = 1; i <= n; i++) dp[i][0] = height[i]; for (int j = 1; (1 << j) <= n; j++) { for (int i = 1; i + (1 << j) - 1 <= n; i++) dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]); } }}int RMQ(int l, int r) { l = rk[l]; r = rk[r]; if (l > r) swap(l, r); int k = 0; while ((1 << (k + 1)) <= r - l) ++k; return min(dp[l + 1][k], dp[r - (1 << k) + 1][k]);}char str[MAXN];int main() { //freopen("in.txt", "r", stdin); int T, cs = 0; scanf("%d", &T); while (T--) { int g; scanf("%d%s", &g, str); int n = strlen(str); for (int i = 0; i < n; i++) s[i] = s[2 * n - i] = str[i] - 'a' + 1; s[n] = 0; s[2 * n + 1] = 27; int m = 2 * n + 1; solve(s, sa, rk, height, m + 1, 28); RMQ_init(m); LL ans = 0; for (int l = 1; l * 2 + g <= n; l++) { for (int i = 0; i < n; i += l) { int j = i + g + l; if (j >= n) break; if (str[i] != str[j]) continue; int lcp0 = min(RMQ(i, j), l); int lcp1 = min(RMQ(n * 2 - i, n * 2 - j), l); //cout << lcp0 << " " << lcp1 << endl; LL tmp = lcp0 + lcp1 - 1; if (tmp >= l) ans += tmp - l + 1; } } printf("Case %d: %lld\n", ++cs, ans); } return 0;}
阅读全文
0 0
- UVA 10829 后缀数组+技巧计数
- UVa 10829 后缀数组+RMQ
- UVA 10829(后缀数组)
- UVA 10829 - L-Gap Substrings(后缀数组)
- uva 10829 - L-Gap Substrings(后缀数组)
- UVA 10829 L-Gap Substrings 后缀数组
- UVA 11855 - Buzzwords(后缀数组)
- uva 11855 - Buzzwords(后缀数组)
- UVA 11107(后缀数组)
- hdu 4622 后缀数组计数问题||后缀自动机
- UVA 10829 L-Gap Substrings(后缀数组好题)
- UVa 10829 - L-Gap Substrings (后缀数组)
- UVA 10829 L-Gap Substrings(后缀数组)
- UVA 10829 L-Gap Substrings(后缀数组+枚举区间优化)
- UVA 10829 L-Gap Substrings (后缀数组+RMQ)
- [sa][后缀数组]关于后缀数组的若干技巧
- UVA 11107(Life Forms-后缀数组+二分)
- UVA 11107 Life Forms 后缀数组
- TMemoryStream、String与OleVariant互转
- android之开机广播播放音乐
- 【学习摘记】马士兵bbs改良版_BBS改良版总结与反思
- BZOJ1051 受欢迎的牛 [Tarjan]
- Spark--Project Tungsten:让Spark将硬件性能压榨到极限
- UVA 10829 后缀数组+技巧计数
- Java 循环
- spring源码分析,重新认识spring 一
- 代码点和代码单元
- ES6语法新特性let和template模板机制
- job的注解为空
- (待修改)FrameLayout实现霓虹灯效果+handleMessage+TimerTask
- bzoj 3531: [Sdoi2014]旅行
- 在线客服系统