本篇是罗穗骞《后缀数组——处理字符串的有力工具》的读书笔记。

知识点:  后缀数组、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的更多相关文章

  1. 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 ...

  2. spoj687 后缀数组重复次数最多的连续重复子串

    REPEATS - Repeats no tags  A string s is called an (k,l)-repeat if s is obtained by concatenating k& ...

  3. SPOJ REPEATS 后缀数组

    题目链接:http://www.spoj.com/problems/REPEATS/en/ 题意:首先定义了一个字符串的重复度.即一个字符串由一个子串重复k次构成.那么最大的k即是该字符串的重复度.现 ...

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

    [题目链接] http://www.spoj.com/problems/REPEATS/en/ [题目大意] 求重复次数最多的连续重复子串的长度. [题解] 考虑错位匹配,设重复部分长度为l,记s[i ...

  5. 687. Repeats spoj (后缀数组 重复次数最多的连续重复子串)

    687. Repeats Problem code: REPEATS A string s is called an (k,l)-repeat if s is obtained by concaten ...

  6. spoj687(后缀数组)

    http://www.spoj.com/problems/REPEATS/ 题意:给一串字符,需要你求这一串字符中有连续重复的字符的重复次数....... 思路:这是和poj3693一种类型的题目.. ...

  7. SPOJ Repeats(后缀数组+RMQ-ST)

    REPEATS - Repeats no tags  A string s is called an (k,l)-repeat if s is obtained by concatenating k& ...

  8. SPOJ - REPEATS —— 后缀数组 重复次数最多的连续重复子串

    题目链接:https://vjudge.net/problem/SPOJ-REPEATS REPEATS - Repeats no tags  A string s is called an (k,l ...

  9. Spoj REPEATS 后缀自动机+set

    REPEATS - Repeats 链接:http://www.spoj.com/problems/REPEATS 题意:求S串中某个子串连续循环次数最多的次数. 想法: 从暴力开始,枚举所有串,求出 ...

随机推荐

  1. 计算3的n次幂htm代码

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 【Linux常见命令】pwd命令

    pwd - print name of current/working directory pwd命令用于显示工作目录. 执行pwd指令可立刻得知您目前所在的工作目录的绝对路径名称. 语法:pwd [ ...

  3. Java泛型和编译优化的一个例子

    public class Main { public static void main(String[] args) { ArrayList<String> strList = new A ...

  4. cookie ,session 和localStorage的区别详解

    2019独角兽企业重金招聘Python工程师标准>>> cookie ,session 和localStorage的区别详解 博客分类: js 当你在浏览网站的时候,WEB 服务器会 ...

  5. c/c++头文件的摘抄

    C/C++常用头文件 以及简单应用介绍 C/C++头文件一览 C #include <assert.h> //设定插入点 #include <ctype.h> //字符处理 # ...

  6. Codeforces 1291 Round #616 (Div. 2) C. Mind Control(超级详细)

    C. Mind Control You and your n−1 friends have found an array of integers a1,a2,-,an. You have decide ...

  7. Floyd —Warshall(最短路及其他用法详解)

    一.多元最短路求法 多元都求出来了,单源的肯定也能求. 思想是动态规划的思想:从任意节点A到任意节点B的最短路径不外乎2种可能,1是直接从A到B,2是从A经过若干个节点X到B.所以,我们假设Dis(A ...

  8. Fiddler 介绍

    1.fiddler原理介绍 fiddler 是一个抓包工具,当浏览器访问服务器会形成一个请求,此时,fiddler就处于请求之间,当浏览器发送请求,会先经过 fiddler,然后在到服务器:当服务器有 ...

  9. pytho xlrd简介

    xlrd:是什么? xlrd是python的一个模块,主要用来对Excel进行读的操作,相对应的,xlwd就是对Excel进行写的模块了. xlrd中有哪些方法可以用呢? 1.打开Excel文件读取数 ...

  10. 题目分享Y

    题意:给出一个n个点n条边的图且不一定连通(原题面为每个节点出度为1),相邻节点不能同时被选,每个节点有其对应价值,求最多能获得多少价值?n<=1e6,val[i]<=1e6 分析:很容易 ...