SPOJ687 Repeats
本篇是罗穗骞《后缀数组——处理字符串的有力工具》的读书笔记。
知识点: 后缀数组、RMQ
解题思路:
枚举长度 \(L\),然后检查长度为 \(L\) 的子串最多能连续重复几次。
对于给定的字符串 \(S\),我们只关注其上坐标为 \(0, L, 2L, ......\) 的点。
如果连续重复子串的首字符恰好在这些点上,那么连续重复子串出现的次数恰好是 \( \frac{lcp(L_1, L_2)}{L} + 1\),(注:\(lcp\) 为 Longest Common Prefix 的简写),如图 1 所示;
否则,我们先计算出 \(lcp(L_1, L_2)\) 中 模 \( L\) 后余下的长度 \(L'\),如图 2 中橙色圈里的片段,可以推测出连续重复子串真正的首字符位于 \( pos = L_1 - (L - L')\),如果 \(0 \le pos\),则连续重复子串出现的次数为:\( \frac{lcp(pos, pos+L)}{L} + 1\)。
记录出现的最多次数,即为答案。最长公共前缀的查询要用 RMQ 优化。

AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm> using namespace std;
const int maxn = + , inf = 0x7fffffff;
int len, tk;
int Rank[maxn], tmp[maxn];
int S[maxn];
int sa[maxn], lcp[maxn]; bool compare_sa(int i, int j) {
if (Rank[i] != Rank[j]) return Rank[i] < Rank[j];
else {
int ri = i + tk <= len ? Rank[i + tk] : -;
int rj = j + tk <= len ? Rank[j + tk] : -;
return ri < rj;
}
} void construct_sa() {
for (int i = ; i <= len; i++) {
sa[i] = i;
Rank[i] = i < len ? S[i] : -;
} for (tk = ; tk <= len; tk *= ) {
sort(sa, sa + len + , compare_sa);
tmp[sa[]] = ;
for (int i = ; i <= len; i++) {
tmp[sa[i]] = tmp[sa[i - ]] + (compare_sa(sa[i - ], sa[i]) ? : );
}
for (int i = ; i <= len; i++) {
Rank[i] = tmp[i];
}
}
} void construct_lcp() {
int h = ;
lcp[] = ;
for (int i = ; i < len; i++) {
int j = sa[Rank[i] - ]; if (h > ) h--;
for (; j + h < len && i + h < len; h++) {
if (S[j + h] != S[i + h]) break;
}
lcp[Rank[i] - ] = h;
}
} int RMQ[maxn];
int mm[maxn], best[][maxn];
void initRMQ(int n) {
mm[] = -;
for (int i = ; i <= n; i++)
mm[i] = ((i&(i - )) == ) ? mm[i - ] + : mm[i - ];
for (int i = ; i <= n; i++) best[][i] = i;
for (int i = ; i <= mm[n]; i++) {
for (int j = ; j + ( << i) - <= n; j++) {
int a = best[i - ][j];
int b = best[i - ][j + ( << (i - ))];
if (RMQ[a] < RMQ[b]) best[i][j] = a;
else
{
best[i][j] = b;
}
}
}
}
int askRMQ(int a, int b) {
int t;
t = mm[b - a + ];
b -= ( << t) - ;
a = best[t][a]; b = best[t][b];
return RMQ[a] < RMQ[b] ? a : b;
}
int find_lcp(int a, int b) {
if (a>b) swap(a, b);
return lcp[askRMQ(a, b - )];
}
int main()
{
char inp[];
int H;
scanf("%d", &H);
while (H--) {
scanf("%d", &len);
for (int i = ; i<len; i++) {
scanf("%s", inp);
if (inp[] == 'a') S[i] = ;
else S[i] = ;
}
construct_sa();
construct_lcp();
for (int i = ; i <= len; i++) RMQ[i] = lcp[i];
initRMQ(len);
int ans = ;
for (int i = ; i <= len; i++) {
int ret = ;
for (int j = ; j + i<len; j += i) {
int r1 = Rank[j], r2 = Rank[j + i];
int L = find_lcp(r1, r2);
int temp = L / i + ;
int k = j - (i - L%i);
if (k >= ) {
temp = find_lcp(Rank[k], Rank[k + i]) / i + ;
}
ret = max(ret, temp);
}
ans = max(ans, ret);
}
printf("%d\n", ans);
}
return ;
}
SPOJ687 Repeats的更多相关文章
- spoj687 REPEATS - Repeats (后缀数组+rmq)
A string s is called an (k,l)-repeat if s is obtained by concatenating k>=1 times some seed strin ...
- spoj687 后缀数组重复次数最多的连续重复子串
REPEATS - Repeats no tags A string s is called an (k,l)-repeat if s is obtained by concatenating k& ...
- SPOJ REPEATS 后缀数组
题目链接:http://www.spoj.com/problems/REPEATS/en/ 题意:首先定义了一个字符串的重复度.即一个字符串由一个子串重复k次构成.那么最大的k即是该字符串的重复度.现 ...
- SPOJ 687 Repeats(后缀数组+ST表)
[题目链接] http://www.spoj.com/problems/REPEATS/en/ [题目大意] 求重复次数最多的连续重复子串的长度. [题解] 考虑错位匹配,设重复部分长度为l,记s[i ...
- 687. Repeats spoj (后缀数组 重复次数最多的连续重复子串)
687. Repeats Problem code: REPEATS A string s is called an (k,l)-repeat if s is obtained by concaten ...
- spoj687(后缀数组)
http://www.spoj.com/problems/REPEATS/ 题意:给一串字符,需要你求这一串字符中有连续重复的字符的重复次数....... 思路:这是和poj3693一种类型的题目.. ...
- SPOJ Repeats(后缀数组+RMQ-ST)
REPEATS - Repeats no tags A string s is called an (k,l)-repeat if s is obtained by concatenating k& ...
- SPOJ - REPEATS —— 后缀数组 重复次数最多的连续重复子串
题目链接:https://vjudge.net/problem/SPOJ-REPEATS REPEATS - Repeats no tags A string s is called an (k,l ...
- Spoj REPEATS 后缀自动机+set
REPEATS - Repeats 链接:http://www.spoj.com/problems/REPEATS 题意:求S串中某个子串连续循环次数最多的次数. 想法: 从暴力开始,枚举所有串,求出 ...
随机推荐
- qemu-img 整理
qemu-img命令语法: qemu-img command [command options] check命令: check [-f fmt < qcow2 | qed | vdi >] ...
- vue中递归组件的使用
2019独角兽企业重金招聘Python工程师标准>>> 递归 简单来讲就是程序自己调用自身. vue中的递归组件就是,组件自身调用自身. 父组件 <template> & ...
- require.context的妙用
比较好用,记录下来. 以下方法将获取vuex中Modules文件夹里的所有modules并导出. const files = require.context(".", false, ...
- CloudCC CRM探讨:精细流程管理与员工悟性培养
很多企业主招聘时更喜欢专业的销售,来给他们创造价值.老板不愿意花时间在"磨刀上",而喜欢员工一来就"砍柴".即使是建立培训机制,仍然很大程度依赖于员工自己的悟性 ...
- 一个页面从输入url到页面加载完成究竟经历了些什么
本人经参考谢希仁著<计算机网络(第 5版)>.<HTTP权威指南>和网络上关于浏览器渲染原理的介绍,结合自己理解,整理出以下结论,如有不正确或者不完善之处欢迎指正: 当用户在浏 ...
- 图论--2-SAT--poj 3678-Katu Puzzle(模板题)
Description Katu Puzzle is presented as a directed graph G(V, E) with each edge e(a, b) labeled by a ...
- HTML(css 样式)
1.CSS 可以通过以下方式添加到 HTML 中: 内联样式 -- 在 HTML 元素中使用 "style" 属性 内部样式表 -- 在 HTML 文档头部 <head> ...
- 深度优先搜索理论基础与实践(java)
概论 深度优先搜索属于图算法的一种,是一个针对图和树的遍历算法,英文缩写为 DFS 即 Depth First Search.深度优先搜索是图论中的经典算法,利用深度优先搜索算法可以产生目标图的相应拓 ...
- java读源码之 Queue(ArrayDeque,附图,希望能一起交流)
除了并发应用(并发包下的代码我之后会专门写),Queue在JavaSE5中仅有的两个实现是LinkedList和PriorityQueue,它们的差异在于排序行为而不是性能.1.6时新增了一个实现Ar ...
- Springboot邮件发送思路分析
毕业设计里需要邮件发送,所以学习,总的来讲,我考虑以下几点, 代码量少,代码简单.配置少,一看就懂,使用 JavaMail 太麻烦了. 异步执行,添加员工之后会发送入职邮件, 多线程处理,设计里有一个 ...