HDU 5558 2015ICPC合肥站G题

题意:给一个字符串加密,加密规则为:

对于第i个字符  如果存在S[T.....T+K-1] == S[i....i+K-1]     则输出T,K  如果有多种方法输出K最大的  如果有多种K最大的方法则输出T最小的。然后把i增加K

如果不存在。则输出-1 和 第i个字符的ASCII码    并且把i增加1.


解法:首先用后缀数组求出 sa  rk  和 height      

然后可知若rk[A]<rk[B] 那么他们的最长公公前缀为 min(height[i])      A<I<=B;

设S(A,B)为A和B的最长公公前缀;

易知   若rk[A]<rk[B]<rk[C] 则  S(A,C)<=S(B,C);

所以对于第i个字符  只需要向左右两边找出最近的并且起始点在你之前的rk即可,可用单调栈维护(我不会)于是傻逼的写了个线段树

找到之后只需要求出一段区间的最小值即可,可用树状数组优化(因为前面写了线段树,所以偷懒就用线段树了)

求出最小值之后就可以通过之前求到的两个rk向外扩展求最小的T即可

写题的时候犯了一个特别傻逼的错误 所以WA了好多发。。。。。。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
#define rep(i,n) for(int i = 0;i < n; i++)
#define CLEAR(a)  memset(a,0,sizeof(a))
using namespace std;
const int N =200005, INF = 1<<30;
int rk[N],sa[N],height[N],w[N],wa[N],res[N];
void getSa (int len,int up) {
    int *k = rk,*id = height,*r = res, *cnt = wa;
    rep(i,up) cnt[i] = 0;
    rep(i,len) cnt[k[i] = w[i]]++;
    rep(i,up) cnt[i+1] += cnt[i];
    for(int i = len - 1; i >= 0; i--) {
            sa[--cnt[k[i]]] = i;
    }
    int d = 1,p = 0;
    while(p < len){
        for(int i = len - d; i < len; i++) id[p++] = i;
        rep(i,len)  if(sa[i] >= d) id[p++] = sa[i] - d;
        rep(i,len) r[i] = k[id[i]];
        rep(i,up) cnt[i] = 0;
        rep(i,len) cnt[r[i]]++;
        rep(i,up) cnt[i+1] += cnt[i];
        for(int i = len - 1; i >= 0; i--) {
            sa[--cnt[r[i]]] = id[i];
        }
        swap(k,r);
        p = 0;
        k[sa[0]] = p++;
        rep(i,len-1) {
                if(sa[i]+d < len && sa[i+1]+d <len &&r[sa[i]] == r[sa[i+1]]&& r[sa[i]+d] == r[sa[i+1]+d])
                        k[sa[i+1]] = p - 1;
                else k[sa[i+1]] = p++;
        }
        if(p >= len) return ;
        d *= 2,up = p, p = 0;
    }
}
void getHeight(int len) {
    rep(i,len) rk[sa[i]] = i;
    height[0] =  0;
    int ans=0;
    for(int i = 0,p = 0; i < len; i++) {
        if(rk[i]==0)  continue;
        int j = sa[rk[i]-1];
        while(i+p < len&& j+p < len&& w[i+p] == w[j+p]) {
                p++;
        }
        height[rk[i]] = p;
        //cout<<i<<" "<<rk[i]<<" "<<p<<endl;
        //if(i<len1-1&&j>=len1)  ans=max(ans,p);
        //else if(i>=len1&&j<len1-1)  ans=max(ans,p);
        p = max(0,p - 1);
    }
    //return ans;
}
struct tree1
{
    int left;
    int right;
    int maxn;
    int minn;
}tre[400005],tre1[400005];
void init(tree1 *tree,int inst,int l,int r)
{
    tree[inst].left=l;
    tree[inst].right=r;
    tree[inst].maxn=-INF;
    tree[inst].minn=INF;
    if(l==r) return ;
    int mid=(l+r)>>1;
    init(tree,2*inst,l,mid);
    init(tree,2*inst+1,mid+1,r);
}
void add(tree1 *tree,int inst,int k,int z)
{
    tree[inst].maxn=max(tree[inst].maxn,z);
    tree[inst].minn=min(tree[inst].minn,z);
    if(tree[inst].left==tree[inst].right) return ;
    int mid=(tree[inst].left+tree[inst].right)>>1;
    if(mid<k) add(tree,2*inst+1,k,z);
    else add(tree,2*inst,k,z);
}
int query(tree1 *tree,int inst,int l,int r,bool flag)
{
    if(tree[inst].left==l&&tree[inst].right==r)
    {
        if(!flag) return tree[inst].maxn;
        else return tree[inst].minn;
    }
    int mid=(tree[inst].left+tree[inst].right)>>1;
    if(r<=mid) return query(tree,2*inst,l,r,flag);
    else if(l>mid) return query(tree,2*inst+1,l,r,flag);
    else
    {
        if(!flag) return max(query(tree,2*inst,l,mid,flag),query(tree,2*inst+1,mid+1,r,flag));
        else return min(query(tree,2*inst,l,mid,flag),query(tree,2*inst+1,mid+1,r,flag));
    }
}
char s[100005];
int main()
{
    int t;
    scanf("%d",&t);
    int cnt=0;
    while(t--)
    {
        scanf("%s",s);
        printf("Case #%d:\n",++cnt);
        int len=strlen(s);
        int maxn=0;
        for(int i=0;i<len;i++)
        {
            w[i]=s[i];
            maxn=max(maxn,w[i]);
        }
        w[len++]=0;
        getSa(len,maxn+1);
        getHeight(len);
        len--;
        init(tre1,1,1,len);
        for(int i=1;i<=len;i++)
        {
            add(tre1,1,i,height[i]);
        }
        init(tre,1,1,len);
        int i=0;
        while(i<len)
        {
            int k=rk[i];
            int l=query(tre,1,1,k,0);
            int r=query(tre,1,k,len,1);
            int res=0;
            int l1=-1,r1=-1;
            if(l!=-INF)
            {
                l1=query(tre1,1,l+1,k,1);
            }
            if(r!=INF)
            {
                r1=query(tre1,1,k+1,r,1);
            }
            res=max(res,max(l1,r1));
            if(res==0)
            {
                printf("-1 %d\n",s[i]);
                add(tre,1,k,k);
                i++;
            }
            else
            {
                int T=len;
                if(res==l1)
                {
                    T=min(sa[l],T);
                    while(l>0&&height[l]>=res)
                    {
                        l--;
                        T=min(T,sa[l]);
                    }
                }
                if(res==r1)
                {
                    T=min(T,sa[r]);
                    while(r<=len&&height[r]>=res)
                    {
                        T=min(T,sa[r]);
                        r++;
                    }
                }
                printf("%d %d\n",res,T);
                while(res--)
                {
                    add(tre,1,k,k);
                    i++;
                    k=rk[i];
                }
            }
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值