ural1297

http://www.elijahqi.win/2017/07/15/ural1297/
1297. Palindrome
Time limit: 1.0 second
Memory limit: 64 MB
The “U.S. Robots” HQ has just received a rather alarming anonymous letter. It states that the agent from the competing «Robots Unlimited» has infiltrated into “U.S. Robotics”. «U.S. Robots» security service would have already started an undercover operation to establish the agent’s identity, but, fortunately, the letter describes communication channel the agent uses. He will publish articles containing stolen data to the “Solaris” almanac. Obviously, he will obfuscate the data, so “Robots Unlimited” will have to use a special descrambler (“Robots Unlimited” part number NPRx8086, specifications are kept secret).
Having read the letter, the “U.S. Robots” president recalled having hired the “Robots Unlimited” ex-employee John Pupkin. President knows he can trust John, because John is still angry at being mistreated by “Robots Unlimited”. Unfortunately, he was fired just before his team has finished work on the NPRx8086 design.
So, the president has assigned the task of agent’s message interception to John. At first, John felt rather embarrassed, because revealing the hidden message isn’t any easier than finding a needle in a haystack. However, after he struggled the problem for a while, he remembered that the design of NPRx8086 was still incomplete. “Robots Unlimited” fired John when he was working on a specific module, the text direction detector. Nobody else could finish that module, so the descrambler will choose the text scanning direction at random. To ensure the correct descrambling of the message by NPRx8086, agent must encode the information in such a way that the resulting secret message reads the same both forwards and backwards.
In addition, it is reasonable to assume that the agent will be sending a very long message, so John has simply to find the longest message satisfying the mentioned property.
Your task is to help John Pupkin by writing a program to find the secret message in the text of a given article. As NPRx8086 ignores white spaces and punctuation marks, John will remove them from the text before feeding it into the program.
Input
The input consists of a single line, which contains a string of Latin alphabet letters (no other characters will appear in the string). String length will not exceed 1000 characters.
Output
The longest substring with mentioned property. If there are several such strings you should output the first of them.
Sample
input output

ThesampletextthatcouldbereadedthesameinbothordersArozaupalanalapuazorA

ArozaupalanalapuazorA

Problem Author: Eugene Krokhalev
Problem Source: IX Open Collegiate Programming Contest of the High School Pupils (13.03.2004)

代码写得有点长,不要怕,主要都是查找错误,调试程序留下来的。
一开始按照课上方法写的,一直有误,网上查找blog发现有的blog也是这么写的,并且用数据zzzdzaadzzz测试,用blog程序跑,出来也是zdz 应该有误
原想法:首先反转,拼接原串与反转串,然后求两者的最长公共前缀,唯一注意的是如果有多组要输出最先出现那一组
问题就出在要求输出最先出现的一组
原程序:

for (int i=1;i<=n;++i){

        if ((sa[i]/n1)^(sa[i-1]/n1)) {
            int a=sa[i-1],b=sa[i];
            if (b<a) swap(b,a);
            if (a+height[i]-1!=n-b+1){continue;}
            if(height[i]>ans) ans=height[i],ans1=b;else if (height[i]==ans )ans1=max(ans1,b),printf("%d\n",b); 
        }

    }
    //printf("%d %d",ans,ans1);
    for (int i=ans1;i<ans1+ans;++i) printf("%c",a[i]);
}

原来的求法:在所有height中寻找最大值,并且满足起始位置在两个串中,且这两个可以构成回文串。在zzzdzaadzzz中先来看一下输出的结果

zzzdaazdzzz0

aadzzz#zzzdaazdzzz0
aazdzzz2
adzzz#zzzdaazdzzz1
azdzzz1
daazdzzz0
dzaadzzz#zzzdaazdzzz1
dzzz2
dzzz#zzzdaazdzzz4
z0
z#zzzdaazdzzz1
zaadzzz#zzzdaazdzzz1
zdaazdzzz1
zdzaadzzz#zzzdaazdzzz2
zdzzz3
zz1
zz#zzzdaazdzzz2
zzdaazdzzz2
zzdzaadzzz#zzzdaazdzzz3
zzz2
zzz#zzzdaazdzzz3
zzzdaazdzzz3
zzzdzaadzzz#zzzdaazdzzz4
13
zdz

这些输出首先按照字典序输出了所有的后缀,在后面的数字表示这个后缀和排序中前一个最长公共前缀即为height

最后一个数即为要输出字符的起始位置

基于这样的方法,好处是height数组已经是按照字典序排序那么可以满足长度最大,不错,不妨可以看一下上方数据红色部分,若有相同的长度的回文,我们要求求出现最早的那个回文,按照原来方法一位贪心(因为我记得位置是后半段中的位置相当于镜像)所以我求位置最大 既然如此,可能会想到,我每次不着急更新前面所存的sa ,但这是不可能的如果我们height的值是3334333那么头尾两个后缀计算的时候一定会因为4的存在而把之前第一位存height[3]给替换掉,或者这个缺点也可以用其他方法,暂时没想清楚,只要将这个问题重新转化为区间求height就可以避免。

如果是和我上面这么写的那么应该WA在第18个点,如果WA在其他点,有可能上面这种写法也没想清楚。

RMQ求解思路:

首先要在本身与镜像之间插入一个特殊的,不可能出现的字符

我们枚举每一个原始字符串中的字符以它为中心(分为奇数和偶数两种情况)进行查找,比如对于下标为i的字符,当回文串为奇数时,我们要求的就是i的后缀与2*n-i的

后缀的最长公共前缀了,然后根据height数组的性质就转化成求height[i+1]…height[2*n-i]中的最小值了,这时我们想到了用RMQ求区间最值了

如果WA在第15 尝试一下这个数据abasajhdabba 应该是abba

注意分清楚奇数和偶数讨论,公式写的时候先认真推导一下。

#include<cstdio>
#include<cstring>
#define N 2200
char a[N];
int rank[N<<1],rank1[N],st[300],count[N],tmp[N],sa[N],k,height[N],fmin[N][30];
inline void swap(int &x,int &y){
    int t=y;y=x;x=t;
}
inline int max(int a,int b){
    return a>b?a:b;
}
inline int min(int a,int b){
    return a<b?a:b;
}
inline int lcp(int l,int r){
    int a=rank[l],b=rank[r];
    if (a>b) swap(a,b);a++;
    int k=0;while ((1<<(k+1))<=(b-a+1))++k;
    return min(fmin[a][k],fmin[b-(1<<k)+1][k]);
}
int main(){
    freopen("ural1297.in","r",stdin);
    freopen("ural1297.out","w",stdout);
    while (~scanf("%s",a+1)){
        int n=strlen(a+1),n1=n;
        a[n+1]='#';
        for (int i=2*n+1;i>n+1;--i) a[i]=a[2*n+2-i];
    //    a[0]='0';
        //printf("%s",a);
        n=2*n+1;
        memset(rank,0,sizeof(rank));
        memset(rank1,0,sizeof(rank1));
        memset(st,0,sizeof(st));
        for (int i=1;i<=n;++i) st[a[i]]=1;
        for (int i=1;i<=255;++i) st[i]+=st[i-1];
        for (int i=1;i<=n;++i) rank[i]=st[a[i]];
        k=0;
        for (int p=1;k!=n;p<<=1){
            memset(count,0,sizeof(count));
            for (int i=1;i<=n;++i) count[rank[i+p]]++;
            for (int i=1;i<=n;++i) count[i]+=count[i-1];
            for (int i=n;i>=1;--i) tmp[count[rank[i+p]]--]=i;
            memset(count,0,sizeof(count));
            for (int i=1;i<=n;++i) count[rank[i]]++;
            for (int i=1;i<=n;++i) count[i]+=count[i-1];
            for (int i=n;i>=1;--i) sa[count[rank[tmp[i]]]--]=tmp[i];
            memcpy(rank1,rank,sizeof(rank)>>1);
            rank[sa[1]]=k=1;
            for (int i=2;i<=n;++i){
                if (rank1[sa[i]]!=rank1[sa[i-1]]||rank1[sa[i]+p]!=rank1[sa[i-1]+p]) ++k;
                rank[sa[i]]=k;
            }
        }
    //    for (int i=1;i<=n;++i) printf("%d ",sa[i]);
        k=0;
        for (int i=1;i<=n;++i){
            if (rank[i]==1){
                height[1]=0;continue;
            }
            k=k==0?0:k-1;
            while (a[i+k]==a[sa[rank[i]-1]+k]) ++k;
            height[rank[i]]=k;
        }
        /*for (int i=1;i<=n;++i){
            for (int j=sa[i];j<=n;++j) printf("%c",a[j]);
            printf("%d ",height[i]);
            printf("\n");
        }/*
        //for (int i=1;i<=n;++i) printf("%d ",height[i]);
        /*int ans=1,n1=n/2,ans1=1;bool flag=false;
        for (int i=1;i<=n;++i){

            if ((sa[i]/n1)^(sa[i-1]/n1)) {
                int a=sa[i-1],b=sa[i];
                if (b<a) swap(b,a);
                if (a+height[i]-1!=n-b+1){continue;}
                if(height[i]>ans) ans=height[i],ans1=a;else if (height[i]==ans )ans1=min(ans1,a),printf("%d\n",a); 
            }

        }
        printf("%d %d",ans,ans1);
        for (int i=ans1;i<ans1+ans;++i) printf("%c",a[i]);*/


        //RMQ
        memset(fmin,0x7f,sizeof(fmin));
        for (int i=1;i<=n;++i) fmin[i][0]=height[i];
        for (int j=1;(1<<j)<=n;++j){
            for (int i=1;i+(1<<j)-1<=n;++i){
                fmin[i][j]=min(fmin[i][j-1],fmin[i+(1<<(j-1))][j-1]);
                //printf("%d ",fmin[i][j]);
            }
        //  printf("\n");
        }
        int ans=0,ans1=0;
        for (int i=1;i<=n;++i){
            int tmp=lcp(i,2*n1-i+2)*2-1;//printf("%d ",tmp);
            if (ans<tmp){//奇数时 
                ans=tmp;ans1=i;
            }
            if(i>1){//i>1时允许有偶数出现 
                tmp=lcp(i,2*n1-i+3)*2; 
                if(ans<tmp){
                    ans=tmp;
                    ans1=i;
                }
            } 

        }
        if (ans%2){
            for (int i=ans1-ans/2;i<=ans1+ans/2;++i) printf("%c",a[i]);
        }else{
            for (int i=ans1-ans/2;i<=ans1+ans/2-1;++i) printf("%c",a[i]);
        }
    }

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值