题目链接:http://poj.org/problem?id=3693
题意:给你一个字符串,让你求重复次数最多的连续重复子串
思路:先穷举长度L,然后求长度为L的子串最多能连续出现几次。首先,肯定能连续出现1次,所以我们直接考虑出现2次及以上的情况。假设在原字符串中连续出现了两次,记这个子字符串为S,那么S肯定包括了字符r[0], r[L], r[2*L], r[3*L], ..... 中相邻的某两个。所以,我们只需看字符r[L*i] 和 r[L*(i+1)] 往期和往后各能匹配多远,记这个总长度为K,那么这里连续出现了 K/L+1次。最后看最大值是多少。如图:
但是这道题还要用RMQ预处理,外加一个反向处理优化,看的bin神的讲解:
AC代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5+5;
int sa[maxn], t1[maxn], t2[maxn], c[maxn];
int ranks[maxn], height[maxn];
void build_sa(int s[], int n, int m)
{
int i, j, p, *x = t1, *y = t2;
for (i = 0; i < m; ++i) c[i] = 0;
for (i = 0; i < n; ++i) c[x[i] = s[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; j <= n; j <<= 1) {
p = 0;
for (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 < m; ++i) c[i] = 0;
for (i = 0; i < n; ++i) c[x[y[i]]]++;
for (i = 1; i < m; ++i) c[i] += c[i-1];
for (i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1;
x[sa[0]] = 0;
for (i = 1; i < n; ++i)
x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+j] == y[sa[i]+j] ? p-1 : p++;
if (p >= n) break;
m = p;
}
}
void getHeight(int s[], int n)
{
int i, j, k = 0;
for (i = 0; i <= n; ++i) ranks[sa[i]] = i;
for (i = 0; i < n; ++i) {
if(k) k--;
j = sa[ranks[i]-1];
while (s[i+k] == s[j+k]) k++;
height[ranks[i]] = k;
}
}
int mm[maxn];
int best[20][maxn];
void RMQ(int n)
{
mm[0] = -1;
for (int i = 1; i <= n; ++i)
mm[i] = ((i & (i-1)) == 0) ? mm[i-1]+1 : mm[i-1];
for (int i = 1; i <= n; ++i) best[0][i] = i;
for (int i = 1; i <= mm[n]; ++i) {
for (int j = 1; j+(1<<i)-1 <= n; ++j) {
int a = best[i-1][j];
int b = best[i-1][j+(1<<(i-1))];
if(height[a] < height[b]) best[i][j] = a;
else best[i][j] = b;
}
}
}
int askRMQ(int a, int b)
{
int t;
t = mm[b-a+1];
b -= (1 << t)-1;
a = best[t][a];
b = best[t][b];
return height[a] < height[b] ? a : b;
}
int lcp(int a, int b)
{
a = ranks[a];
b = ranks[b];
if(a > b) swap(a, b);
return height[askRMQ(a+1, b)];
}
char str[maxn];
int r[maxn], a[maxn];
int main()
{
int cas = 0;
while (~scanf("%s", str)) {
if(strcmp(str, "#") == 0) break;
cas++;
int n = strlen(str);
for (int i = 0; i <= n; ++i) r[i] = str[i];
build_sa(r, n+1, 128);
getHeight(r, n);
RMQ(n);
int cnt = 0, maxs = 0;
for (int j = 1; j < n; ++j) {
for (int i = 0; i + j < n; i+=j) {
int t1 = lcp(i, i+j);
int step = t1/j + 1;
int k = i - (j-t1 % j);
if(k >= 0 && t1 % j){
if(lcp(k, k+j) >= t1) step++;
}
if(step > maxs){
maxs = step;
cnt = 0;
a[cnt++] = j;
}
else if(step == maxs) a[cnt++] = j;
}
}
int len = -1, st;
for (int i = 1; i <= n && len == -1; ++i) {
for (int j = 0; j < cnt; ++j) {
int l = a[j];
if(lcp(sa[i], sa[i]+l) >= (maxs-1)*l){
len = l;
st = sa[i];
break;
}
}
}
str[st+len*maxs] = 0;
printf("Case %d: %s\n", cas, str+st);
}
return 0;
}