题意:给你一个字符串(len<=1e5),求重复次数最多的连续重复子串。次数最多的不止一个的话,输出字典序最小的重复子串。
思路:求重复最多好计算,直接题目有做到(点击打开链接)但是要输出一个字典序最小的一开始没弄明白。首先存起来最多次数
可能的长度,然后枚举sa数组,取到的第一组,肯定是字典序最小的。但是怎么算取到? 就是LCP(sa[i]+1, sa[i]+1+len) >=
(ans-1)*a[j],即这一段重复达到了最多次。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 3e5+5;
int t1[maxn], t2[maxn], c[maxn];
int ra[maxn], height[maxn];
int sa[maxn];
char str[maxn];
bool cmp(int *r, int a, int b, int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(char str[], int sa[], int ra[], int height[], int n, int m)
{
n++;
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]=str[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]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
if(p >= n) break;
m = p;
}
int k = 0;
n--;
for(i = 0; i <= n; i++) ra[sa[i]] = i;
for(i = 0; i < n; i++)
{
if(k) k--;
j = sa[ra[i]-1];
while(str[i+k]==str[j+k]) k++;
height[ra[i]] = k;
}
}
int lcp[maxn][32];
void initRMQ(int n)
{
for(int i = 1; i <= n; i++) lcp[i][0] = height[i];
for(int i = 1; (1<<i) <= n; i++)
for(int j = 1; j+(1<<i)-1 <= n; j++)
lcp[j][i] = min(lcp[j][i-1], lcp[j+(1<<i-1)][i-1]);
}
//传进去的坐标是从1-n
int LCP(int a, int b)
{
a--, b--;
int l = min(ra[a], ra[b])+1, r = max(ra[a], ra[b]);
int len = r-l+1, i;
for(i = 0; (1<<i) <= len; i++) ;
i--;
return min(lcp[l][i], lcp[r-(1<<i)+1][i]);
}
int a[maxn];
void solve(int n)
{
int ans = 0, cnt = 0;
for(int l = 1; l <= n; l++)
for(int i = 1; i+l <= n; i+=l)
{
int r = LCP(i, i+l);
if(r/l+1 > ans)
{
ans = r/l+1;
cnt = 0;
a[cnt++] = l;
}
else if (r/l+1 == ans) a[cnt++] = l;
if(i-l+r%l > 0)
{
if(LCP(i-l+r%l, i+r%l)/l+1 > ans)
{
ans = LCP(i-l+r%l, i+r%l)/l+1;
cnt = 0;
a[cnt++] = l;
}
else if(LCP(i-l+r%l, i+r%l)/l+1 == ans) a[cnt++] = l;
}
}
int anss = 0, ansl = n;
for(int i = 1; i <= n; i++)
for(int j = 0; j < cnt; j++)
{
int k = LCP(sa[i]+1, sa[i]+a[j]+1); //千万不能少加1
if(k >= (ans-1)*a[j])
{
anss = sa[i];
ansl = a[j]*ans;
i = n+1;
break;
}
}
for(int i = anss; i < anss+ansl; i++)
printf("%c", str[i]);
puts("");
}
int main(void)
{
int ca = 1;
while(~scanf(" %s", str))
{
int len = strlen(str);
if(len == 1 && str[0] == '#')
break;
da(str, sa, ra, height, len, 256);
initRMQ(len);
printf("Case %d: ", ca++);
solve(len);
}
return 0;
}