Hdu 5558 Alice's Classified Message(后缀数组,每个点的前面点和它匹配的最长长度)

传送门:Hdu 5558 Alice’s Classified Message

题意:给你一个字符串(长度小于等于1e5),从起点为0出发,每次判断这个点和其前面的点的最长子串匹配,如果最长子串匹配长度为0,下一个起点为i+1(i为现在的起点),输出-1和当前起点的字母的ASCII码,否则下一个起点为i+最大子串匹配长度,输出最大匹配长度和当前最大子串匹配长度下最左边的点(匹配长度不变的前提小,越小越好)。

我们可以发现,和当前这个点的匹配长度最大的那些点一定是那些和这个点的Rank值差最小的(就是最多有两个点,一个Rank比它大,一个Rank比它小),所以我们先利用了一个set维护这个点前面的那些点的Rank。
通过前面这一部分我们可以求出最长匹配长度为k,然后再利用二分Rank值算出和这个点的最大匹配长度为k的Rank值的L和R,再通过线段树求出[L,R]的最小值

时间复杂度:n*logn 常数比较大

#include<algorithm>
#include<cstdio>
#include<set>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=100100;
/*
*倍增算法nlogn
*将待排序数组放在0~n-1中,在最后补一个0
*build(,n+1,);//注意是n+1
*getHeight(,n);
*例如:
*n   = 8;
*num[]   = { 1, 1, 2, 1, 1, 1, 1, 2, $ };注意num最后一位为0,其他大于0
*Rank[]  = { 4, 6, 8, 1, 2, 3, 5, 7, 0 };Rank[0~n-1]为有效值,Rank[n]必定为0无效值
*sa[]    = { 8, 3, 4, 5, 0, 6, 1, 7, 2 };sa[1~n]为有效值,sa[0]必定为n是无效值
*height[]= { 0, 0, 3, 2, 3, 1, 2, 0, 1 };height[2~n]为有效值
*/
int s[maxn];
int sa[maxn],t1[maxn],t2[maxn],c[maxn];
//构造字符串S的后缀数组,每个字符值必须为0~m-1

void build_sa(int s[],int n,int m){
    int *x=t1,*y=t2;
    for(int i=0;i<m;i++)    c[i]=0;
    for(int i=0;i<n;i++)    c[x[i]=s[i]]++;
    for(int i=1;i<m;i++)    c[i]+=c[i-1];
    for(int i=n-1;i>=0;i--)     sa[--c[x[i]]]=i;
    for(int k=1;k<=n;k<<=1){
        int p=0;
        for(int i=n-k;i<n;i++)  y[p++]=i;
        for(int i=0;i<n;i++)    if(sa[i]>=k)    y[p++]=sa[i]-k;
        for(int i=0;i<m;i++)    c[i]=0;
        for(int i=0;i<n;i++)    c[x[y[i]]]++;
        for(int i=0;i<m;i++)    c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--)    sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(int i=1;i<n;i++)
            x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k] ? p-1:p++;
        if(p>=n)    break;
        m=p;
    }
}

int Rank[maxn],height[maxn];

void getHeight(int s[],int n){
    int k=0;
    for(int i=0;i<=n;i++)   Rank[sa[i]]=i;
    for(int i=0;i<n;i++){
        if(k)   k--;
        int j=sa[Rank[i]-1];
        while(s[i+k]==s[j+k])   k++;
        height[Rank[i]]=k;
    }
}

int dp[maxn][20];

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 N;
int rmq(int L,int R){
    if(L==R)
        return N-L+1;
    L=Rank[L],R=Rank[R];
    if(L>R)
        swap(L,R);
    L++;
    int k=(int)log2(R-L+1);
    return min(dp[L][k],dp[R-(1<<k)+1][k]);
}

char str[maxn];
set<int>st;

int minv[4*maxn];
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

void pushup(int rt){
    minv[rt]=min(minv[rt<<1],minv[rt<<1|1]);
}

void build(int l,int r,int rt){
    if(l==r){
        minv[rt]=sa[l];
        return ;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt);
}

int L,R;
int query(int l,int r,int rt){
    if(L<=l&&R>=r)
        return minv[rt];
    int m=l+r>>1;
    int ans=200000000;
    if(L<=m)
        ans=query(lson);
    if(R>m&&minv[rt<<1|1]<ans)
        ans=min(ans,query(rson));
    return ans;
}

int solve(int now,int ans,int n){
    int low=0,high=Rank[now],st=now;
    while(high-low>=0){ //二分的是Rank值
        int mid=low+high>>1;
        if(rmq(now,sa[mid])>=ans)
            st=mid,high=mid-1;
        else
            low=mid+1;
    }
    low=Rank[now],high=n;
    int ed=now;
    while(high-low>=0){
        int mid=low+high>>1;
        if(rmq(now,sa[mid])>=ans)
            ed=mid,low=mid+1;
        else
            high=mid-1;
    }
    L=st,R=ed;
    return query(1,n,1);
}

int main(){
    int _;
    scanf("%d",&_);
    for(int case1=1;case1<=_;case1++){
        printf("Case #%d:\n",case1);
        st.clear();
        scanf("%s",str);
        int n=strlen(str);
        for(int i=0;i<=n;i++)
            s[i]=str[i];
        build_sa(s,n+1,128);
        getHeight(s,n);
        build(1,n,1);
        RMQ_init(n);
        N=n;
        int now=0,tmp;
        while(now<n){
            set<int>::iterator it=st.lower_bound(Rank[now]);
            if(st.size()==0){
                tmp=now+1;
                printf("-1 %d\n",str[0]);
            }
            else if(it==st.begin()){
                int ans=rmq(now,sa[*it]);
                if(ans==0)
                    tmp=now+1,printf("-1 %d\n",str[now]);
                else
                    tmp=now+ans,printf("%d %d\n",ans,solve(now,ans,n));
            }
            else if(it==st.end()){
                it--;
                int ans=rmq(now,sa[*it]);
                if(ans==0)
                    tmp=now+1,printf("-1 %d\n",str[now]);
                else
                    tmp=now+ans,printf("%d %d\n",ans,solve(now,ans,n));
            }
            else{
                int ans=rmq(now,sa[*it]);
                it--;
                ans=max(ans,rmq(now,sa[*it]));
                if(ans==0)
                    tmp=now+1,printf("-1 %d\n",str[now]);
                else
                    tmp=now+ans,printf("%d %d\n",ans,solve(now,ans,n));
            }
            for(int i=now;i<tmp;i++)
                st.insert(Rank[i]);
            now=tmp;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值