SPOJ #687 Repeats

题目描述:

给定字符串,求字符串中最大的连续重复子串出现次数是多少。

解题思路:

如果一个重复子串的长度是 l,那么它一定跨过s[0]、s[l]、s[l * 2]、s[l * ...] 中连续的两个,那我们就可以枚举 l,枚举起始位置 i * l,用 SA 求出s[i * l]、s[i * l + l]的lcp,那么 lcp / l + 1就是答案。对吗?不一定,有可能s[i * l]前面有段可以匹配,那我们就将位置前移多匹配,但不足l的多出来的那段。再求次lcp计算。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5
 6 const int N = 5e4 + 10;
 7 int T, n, m, st[N][20], ans;
 8 int sa[N], height[N], rank[N], tax[N], tp[N];
 9 char s[N];
10
11 void rsort() {
12     for (int i = 0; i <= m; i ++) tax[i] = 0;
13     for (int i = 1; i <= n; i ++) tax[rank[tp[i]]] ++;
14     for (int i = 1; i <= m; i ++) tax[i] += tax[i - 1];
15     for (int i = n; i >= 1; i --) sa[tax[rank[tp[i]]] --] = tp[i];
16 }
17
18 int cmp(int *f, int x, int y, int w) {return f[x] == f[y] && f[x + w] == f[y + w];}
19
20 void SA() {
21     for (int i = 1; i <= n; i ++) rank[i] = s[i] - ‘a‘ + 1, tp[i] = i;
22     m = 2, rsort();
23     for (int w = 1, p = 1, i; p < n; w <<= 1, m = p) {
24         for (p = 0, i = n - w + 1; i <= n; i ++) tp[++ p] = i;
25         for (int i = 1; i <= n; i ++) if (sa[i] > w) tp[++ p] = sa[i] - w;
26         rsort(), swap(rank, tp), rank[sa[1]] = p = 1;
27         for (int i = 2; i <= n; i ++) rank[sa[i]] = cmp(tp, sa[i], sa[i - 1], w) ? p : ++ p;
28     }
29     int j, k = 0;
30     for (int i = 1; i <= n; height[rank[i ++]] = k)
31         for (k = k ? k - 1 : k, j = sa[rank[i] - 1]; s[i + k] == s[j + k]; k ++);
32 }
33
34 void st_init() {
35     for (int i = 1; i <= n; i ++) st[i][0] = height[i];
36     for (int j = 1; (1 << j) <= n; j ++)
37         for (int i = 1; i + (1 << j) - 1 <= n; i ++) st[i][j] = min(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
38 }
39
40 int rmq(int l, int r) {
41     if (l > r) swap(l, r);
42     l ++;
43     int k = 0;
44     while (1 << k + 1 <= r - l + 1) k ++;
45     return min(st[l][k], st[r - (1 << k) + 1][k]);
46 }
47
48 void work() {
49     ans = 0;
50     for (int l = 1; l <= n; l ++)
51         for (int i = 1; i + l <= n; i += l) {
52             int k, r;
53             k = rmq(rank[i], rank[i + l]);
54             r = k % l;
55             if (r) k = max(k, rmq(rank[i - l + r], rank[i + r]));
56             ans = max(ans, k / l + 1);
57         }
58     printf("%d\n", max(ans, 1));
59 }
60
61 int main() {
62     scanf("%d", &T);
63     while (T --) {
64         scanf("%d\n", &n);
65         scanf("%s", s + 1);
66         SA();
67         st_init();
68         work();
69     }
70     return 0;
71 }
时间: 2024-08-24 11:58:35

SPOJ #687 Repeats的相关文章

SPOJ 687. Repeats(后缀数组求最长重复子串)

题目大意:给你一个串让你求出重复次数最多的连续重复子串的重复次数. 解题思路:论文上给出的解答是: 这还没完,因为经过这两个点的情况还不完备,应还可以假设起点在 [ i*j-i+1, i*j-d],其中 d = i-L/i (d = i-L%i)其意义为根据已知的匹配长度,可以将起点往前移动的范围,太靠后将不能够构造出比之前更好的解.如果要求出某个最多的连续重复子串的最小字典序子需要枚举所有起点,但如果只是要的到最多的重复次数或者任意最多的连续重复子串,那么只需要枚举i*j-d处的起点即可,因为

SPOJ 687 Repeats(后缀数组+ST表)

[题目链接] http://www.spoj.com/problems/REPEATS/en/ [题目大意] 求重复次数最多的连续重复子串的长度. [题解] 考虑错位匹配,设重复部分长度为l,记s[i]和s[i+l]前缀匹配得到的最长长度为r,枚举所有的l和i,得到r,那么答案就是r/l+1的最大值.计算任意后缀的最长公共前缀可以利用后缀数组+ST表来解决,两个后缀的最长公共前缀就是他们名次之间的h数组的最小值. 显然,枚举i和l的复杂度达到了O(n2),是没有办法完成统计的,我们发现每个区段只

SPOJ 687 Repeats 后缀数组

和上一题差不多的方法..没什么好说的 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = (5e4 + 10) * 4; #define F(x) ((x) / 3 + ((x) % 3 == 1 ? 0 : tb)) #define G(x) ((x) < tb ? (x) * 3 + 1 : ((x) - tb) * 3

SPOJ题目687 Repeats(后缀数组+RMQ求重复次数最多的子串的重复次数)

REPEATS - Repeats no tags A string s is called an (k,l)-repeat if s is obtained by concatenating k>=1 times some seed string t with length l>=1. For example, the string s = abaabaabaaba is a (4,3)-repeat with t = aba as its seed string. That is, the

后缀数组 &amp; 题目

后缀数组被称为字符串处理神器,要解决字符串问题,一定要掌握它.(我这里的下标全部都是从1开始) 首先后缀数组要处理出两个数组,一个是sa[],sa[i]表示排名第i为的后缀的起始位置是什么,rank[i]表示第i个字符为起始点的后缀,它的排名是什么.可以知道sa[rank[i]] = i; rank[sa[i]] = i; 由于每个后缀各不相同,至起码长度不同,所以每个后缀是不可能相等的. 解除一个值,就能在O(n)时间内得到另外一个. 定义:suffix(i)表示从[i, lenstr]这个后

POJ 3693 后缀数组+RMQ

点击打开链接 题意:问连续重复部分最多的串是什么,不能重叠,且我们要字典序最小的串如xbcabcab,有bcabca重复次数为2,cabcab重复次数也为2,那么要前边那个 思路:以前写过一个类似的,SPOJ 687,这个只是求连续重复部分最多的串的次数,并不需要将按字典序最小串输出,那么我们可以用到SPOJ687的代码,用它我们可以求出那个重复的次数和满足这个次数的串的长度,那么就只差找到字典序最小的那个串了,而我们知道后缀数组的sa数组就是按字典序来的嘛,从字典序最小开始找,找到就跳出,输出

【转】后缀数组解题总结

之前觉得后缀自动机会了,就忽略了后缀数组,现在发现后缀数组+二分的功能很强,而且后缀自动机好像实现不了. 转发一下,方便队友大概看一下.这几天我也尽快恶补一下. (找不到原博主网站了,失误) 后缀数组解题总结: 1.求单个子串的不重复子串个数.SPOJ 694.SPOJ 705. 这个问题是一个特殊求值问题.要认识到这样一个事实:一个字符串中的所有子串都必然是它的后缀的前缀.(这句话稍微有点绕...)对于每一个sa[i]后缀,它的起始位置sa[i],那么它最多能得到该后缀长度个子串(n-sa[i

SPOJ SUBST1 POJ 2406 POJ REPEATS 后缀数组小结

//聪神说:做完了题目记得总结,方便以后复习. SPOJ SUBST1 题目链接:点击打开链接 题意:给一个字符串,求不同子串个数. 思路:假设所有子串都不同,答案为len*(len+1)/2;然而不是这样... 下面我们就找出重复的子串: 首先先将后缀排序,对于后缀i能生成len-sa[i]个子串,这其中有height[i]个子串与第i-1个后缀生成的子串重复了: 所以答案为 len*(len+1)/2-segema(height[i]) . cpp代码: //spoj disubstr #i

SPOJ Repeats(后缀数组+RMQ)

REPEATS - Repeats no tags A string s is called an (k,l)-repeat if s is obtained by concatenating k>=1 times some seed string t with length l>=1. For example, the string s = abaabaabaaba is a (4,3)-repeat with t = aba as its seed string. That is, the