POJ - 3693 Maximum repetition substring 后缀数组 分块

题意:给一个字符串,求重复次数最多的连续重复子串,如有多解,输出字典序最小的。






思路:

先后缀数组求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("");
    }
}






猜你喜欢

转载自blog.csdn.net/c6376315qqso/article/details/78152041