首页 > 学院 > 开发设计 > 正文

HDU 2459 PKU 3693 Maximum repetition substring 后缀数组 RMQ

2019-11-14 10:52:59
字体:
来源:转载
供稿:网友

题目地址:

HDU 2459 PKU 3696

题意:

给一个长度不超过1e5的字符串,找出其中一个子串,要求该子串能够被分割成尽可能多的相同的字符串,若有多个符合要求的子串,输出字典序最小的那个。

思路:

图片不看也没关系,但这个是罗穗骞大佬在他的论文《后缀数组——处理字符串的有力工具》里的原话,然后这是他的spoj687的代码:http://paste.Ubuntu.com/23923746/,本人就是参考他的代码,明白了思路的 论文第19页

RMQ部分只是用来求lcp的,不说了 枚举子串重复部分的长度L,然后以L为步长,枚举题目所给的字符串下标,设lcp是suffix[L]和suffix[2 * L]的公共前缀,只要lcp>=L,重复的字符串就出现了(首先两者相同的部分超过了suffix[L]和suffix[2 * L]的距离,然后suffix[2 * L]作为suffix[L] 的子串,重复lcp部分,就构成了一个有重复的字符串,可能这里有点绕……不懂的读者可以自己举例理解下)。 重复部分的重复次数自然就是lcp/L+1(两字符串公共部分的长度 / 重复部分的长度 + suffix[L]到suffix[2 * L]之间的子串个数) 然后考虑一个特殊情况,比如说如下字符: 0123123123 就是按上述方法计算的话,算出来的字符是312312,原因是因为枚举时出了问题(毕竟不是一个个枚举的,而是有步长的枚举),考虑suffix[3]与suffix[L]的lcp长度为4,比枚举的长度3多了一个1,可以往前再走L−lcp步(1+2==3),看看这个后缀与其后面L的后缀的lcp长度是否大于等于L−lcp,如果比前面走的步数要大的话,说明少计算了一次重复部分。 输出答案的部分参考了cxlove的,本来自己是特判加枚举子串时直接判定答案的,可以不知道哪里写丑了,总是有问题,后面就换成了cxlove的思路QAQ,他的题解:http://blog.csdn.net/acm_cxlove/article/details/7941205

可能说的不好,还请读者多多思考

代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int MAXN = 1e5 + 5;char str[MAXN];int sa[MAXN], Rank[MAXN], height[MAXN], wv[MAXN], c[MAXN];int d[MAXN][20];int cnt, stk[MAXN];bool cmp(int *r, int a, int b, int l) { return r[a] == r[b] && r[a + l] == r[b + l];}void da(char *r, int n, int m) { int i, j, p, *x = Rank, *y = height; for (i = 0; i < m; ++i) c[i] = 0; for (i = 0; i < n; ++i) ++c[x[i] = r[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, p = 1; p < n; j *= 2, 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) wv[i] = x[y[i]]; for (i = 0; i < m; ++i) c[i] = 0; for (i = 0; i < n; ++i) ++c[wv[i]]; for (i = 1; i < m; ++i) c[i] += c[i - 1]; for (i = n - 1; i >= 0; --i) sa[--c[wv[i]]] = y[i]; for (swap(x, y), 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++; }}void calheight(char *r, int n) { int i, j, k = 0; 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]; r[i + k] == r[j + k]; ++k);}void RMQ_init(int *a, int n) { for (int i = 0; i < n; ++i) d[i][0] = a[i]; for (int j = 1; (1 << j) <= n; ++j) for (int i = 0; i + (1 << j) - 1 < n; ++i) d[i][j] = min(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]);}int RMQ(int L, int R) { L = Rank[L], R = Rank[R]; if (L > R) swap(L, R); ++L; int k = 0; while ((1 << (k + 1)) <= R - L + 1) ++k; return min(d[L][k], d[R - (1 << k) + 1][k]);}int main() { //freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); int kase = 0; while (~scanf("%s", str)) { if (str[0] == '#') break; int len = strlen(str), maxr = 0, k, jj, now; da(str, len + 1, 'z' + 1); calheight(str, len); RMQ_init(height, len + 1); for (int i = 1; i < len; ++i) { for (int j = 0; j + i < len; j += i) { k = RMQ(j, j + i); now = k / i + 1; jj = j - (i - k % i); if (jj >= 0 && RMQ(jj, jj + i) >= (i - k % i)) ++now; if (now > maxr) { cnt = 0; maxr = now; stk[cnt++] = i; } else if (now == maxr) { stk[cnt++] = i; } } } for (int i = 1; i <= len; ++i) { for (int j = 0; j < cnt; ++j) { if (RMQ(sa[i], sa[i] + stk[j]) >= (maxr - 1) * stk[j]){ jj = sa[i]; k = stk[j]; goto END; } } } END: PRintf("Case %d: ", ++kase); for (int i = 0; i < maxr * k; ++i) putchar(str[jj++]); putchar(10); }}
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表