题意:给一个字符串,求重复次数最多的连续重复子串,如有多解,输出字典序最小的。
思路:
先后缀数组求height,然后考虑分块,枚举重复子串的长度l,则每个重复子串必定经过(0,l,2*l,3*l.....)中的一个点,然后对每两个相邻的这样的点求LCP(i,i+L),这是向后匹配能匹配多少,如果忽略i前面的部分,答案是LCP(i,i+L)/L+1,但是如何计算i前面的部分呢?如果LCP(i,i+L)%L>0,说明有剩余left,而剩余的要能和前面的匹配,则有RMQ(i-(L-left),i-(L-left)+L)>lcp才行。
经过上面的操作,能够求出最大重复次数,然后我们能达到最大次数的所有L,再遍历一遍求出最小Rank,即是字典序最小的串。注意这步操作不能在上面求最大重复次数的时候统一求,因为我们虽然能求出最大重复次数,但是却不能保证字典序最小的串在(L,2*L,3*L...)处开始,比如aababababa,此时答案是abababab,而不是babababa。
这个题细节、证明、技巧多,思维量大,写了半天时间。
还有就是最好去HDU - 2459提交下这个题,因为POJ这个题数据太水
感觉这个题要比UVA - 10829难,不过思路都是对L分块。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <cstring>
#include <string>
#include <cmath>
#include <map>
#include <queue>
#include <set>
#include <bitset>
#include <stack>
#include <sstream>
#define IO ios::sync_with_stdio(false),cin.tie(0);
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define debug(x) cout<<#x<<" = "<<"["<<x<<"]"<<endl;
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
typedef long long ll;
using namespace std;
const double eps=1e-7;
const int MOD=1e9+7;
const ll INFLL=0x3f3f3f3f3f3f3f3f;
const int INF=0x3f3f3f3f;
const int MAXN=1e5+5;
int sa[MAXN], Rank[MAXN], height[MAXN];
int wa[MAXN], wb[MAXN], wv[MAXN], wd[MAXN];
char s[MAXN];
int cmp(int *r, int a, int b, int l) {
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(char *r, int n, int m=128) { // 倍增算法 r为待匹配数组 n为总长度 要+1 m为字符范围
int i, j, p, *x = wa, *y = wb, *t;
for(i = 0; i < m; i ++) wd[i] = 0;
for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++;
for(i = 1; i < m; i ++) wd[i] += wd[i-1];
for(i = n-1; i >= 0; i --) sa[-- wd[x[i]]] = i;
for(j = 1, p = 1; p < n; j *= 2, m = p) {
for(p = 0, 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 < n; i ++) wv[i] = x[y[i]];
for(i = 0; i < m; i ++) wd[i] = 0;
for(i = 0; i < n; i ++) wd[wv[i]] ++;
for(i = 1; i < m; i ++) wd[i] += wd[i-1];
for(i = n-1; i >= 0; i --) sa[-- wd[wv[i]]] = y[i];
for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i ++) {
x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1: p ++;
}
}
}
void calHeight(char *r, int n) { // 求height数组。n不用+1
int i, j, k = 0;
for(i = 1; i <= n; i ++) Rank[sa[i]] = i;
for(i = 0; i < n; height[Rank[i ++]] = k) {
for(k ? k -- : 0, j = sa[Rank[i]-1]; r[i+k] == r[j+k]; k ++);
}
}
int dp[MAXN][50];
void RMQ_init(int n) {
for(int i=1; i<=n; i++) dp[i][0]=height[i];
for(int j=1; (1<<j)<=n; j++)
for(int i=1; i+(1<<j)-1<=n; i++)
dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
int RMQ(int L,int R) {
int k=0,t=L;
L=min(Rank[L],Rank[R]);
R=max(Rank[t],Rank[R]);
while((1<<(k+1))<=R-L) k++;
return min(dp[L+1][k],dp[R-(1<<k)+1][k]);
}
int le[MAXN];
int main() {
int cas=1;
while(~scanf("%s",s)) {
if(s[0]=='#')break;
int len=strlen(s);
da(s,len+1),calHeight(s,len);
RMQ_init(len);
int ans=1,cnt=0;
for(int l=1; l*2<=len; l++) {
for(int i=0; i+l<len; i+=l) {
if(s[i]==s[i+l]) {
int lcp=RMQ(i,i+l);
int repeat=lcp/l+1,left=lcp%l;
if(left&&i-(l-left)>=0&&RMQ(i-(l-left),i+left)>lcp)repeat++;
if(ans<repeat) {
ans=repeat;
cnt=0;
le[cnt++]=l;
}else if(ans==repeat){
le[cnt++]=l;
}
}
}
}
cnt=unique(le,le+cnt)-le;
int loc=1,ansl=1,flag=0;
for(int i=0;i<len;i++){
for(int j=0;j<cnt;j++){
if(RMQ(i,i+le[j])/le[j]+1==ans){
if(loc>Rank[i]||!flag){
flag=1;
loc=Rank[i];ansl=le[j];
}
}
}
}
printf("Case %d: ",cas++);
for(int i=sa[loc];i<sa[loc]+ansl*ans;i++)printf("%c",s[i]);
puts("");
}
}