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串中某个子串连续循环次数最多的次数. 想法: 从暴力开始,枚举所有串,求出 ...
随机推荐
- 解决Vue-cli3.0下scss文件编译过慢、卡顿问题
在使用Vue-cli 3.0构建的项目中,可能存在项目编译过慢的问题,具体表现在编译时会在某一进度比如40%时停顿,等好一会儿才能够编译完成.这使得浏览器中的实时预览也会卡顿,不利于我们快速查看效果, ...
- 无法打开到SQL Server的连接 (Microsoft SQL Server, 错误:53) .
标题: 连接到服务器 ------------------------------ 无法连接到 MSSQLSERVER. ------------------------------ 其他信息: 在与 ...
- Intellij-IDEA-maven+springMVC+mybatis整合
2019独角兽企业重金招聘Python工程师标准>>> GitHub地址 https://github.com/Ethel731/WebProjectDemo 前言 之前都是在已经建 ...
- Function-time()
time()函数返回自1970年1月1日0点以来经过的秒数,每秒变化一次? time()函数定义在头文件<time.h>中,原型是: time_t time(time_t *arg); 如 ...
- 解决vue页面刷新或者后退参数丢失的问题
原文链接: 点我 在商城类的项目中,会经常遇到列表数据筛选查询的情景,当要打开某一项的详情页或者暂时离开列表页,再返回(后退时),选择的筛选条件会全部丢失,辛辛苦苦选择好的条件全没了,还得重新选择,如 ...
- 算法竞赛进阶指南--在单调递增序列a中查找>=x的数中最小的一个(即x或x的后继)
while (l < r) { int mid = (l + r) / 2; if (a[mid] >= x) r = mid; else l = mid + 1; }
- C++ 重载运算符 继承 多态 (超详细)
(一)重载运算符: (1)声明与定义格式 一般是类内声明,类外定义,虽然可以在类内定义,但 写前面堆一堆不好看!!! 类内声明: class Demo { 返回值类型 operator 运算符(形参表 ...
- linux 进程管理和内存分配
1.进程相关概念 进程:正在运行中的程序 内核功用:进程管理.文件系统.网络功能.内存管理.驱动程序.安全功能等 Process:运行中的程序的一个副本,是被载入内存的一个指令集合 进程 ID(Pro ...
- linux命令之df dh
df -h, --human-readable 查看磁盘空间占用情况 df -h du -h, --human-readable -s, --summarize 查看文件大小 du -h test.t ...
- 这是一篇每个人都能读懂的最小生成树文章(Kruskal)
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法和数据结构专题的第19篇文章,我们一起来看看最小生成树. 我们先不讲算法的原理,也不讲一些七七八八的概念,因为对于初学者来说,看到 ...