湖南大学第十五届程序设计竞赛(最长回文公共子串)H-Longest Common Palindrome Substring(强大的思维+哈希+马拉车)

https://ac.nowcoder.com/acm/contest/908/H

题意:就是求两个串的最长公共回文子串;

做法:当时那道题撒逼了,以为是一道后缀数组题,结果写了一个小时没写出来,但不过又有人用后缀数组过了,果然自己太弱了。后来想到了马拉车和哈希,这个思路是正确的。就是对任意一个串进行马拉车。找出来他的每个位置的最长回文子串,然后公共回文子串的长度只能其中的一些公共回文子串的长度L,L-2,L-4等等,然后二分答案加hash进行判断就可以了。

但不过我还是WA了很久,我想到了奇偶回文的情况,但不过还是错了,写得不够好吧。后来我偶然想起曾经看过大佬在字符串中插入一些字符,避免奇偶回文的情况。结果还是改了很久,后来有因为数组开小了错了几发,才过了。总结这道题看似简单其实并不简单,还有很多的坑。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=100010;
char s[N<<1],ss[N<<1],s1[N],s2[N],str[N<<2];
int p[N<<2],len1,len2,tot;
ull hash1[N<<1],hash2[N<<1],po[N<<1];
ull geths1(int l,int r)
{
    if(l==0)
        return hash1[r];
    return hash1[r]-hash1[l-1]*po[r-l+1];
}
ull geths2(int l,int r)
{
    if(l==0)
        return hash2[r];
    return hash2[r]-hash2[l-1]*po[r-l+1];
}
int manacher(int len)
{
    int id=0,mx=0,m_len=0;
    for(int i=2; i<len; i++)
    {
        if(mx>i)
            p[i]=min(p[2*id-i],mx-i);
        else
            p[i]=1;
        while(str[i+p[i]]==str[i-p[i]])
            p[i]++;
        if(i+p[i]>mx)
        {
            mx=i+p[i];
            id=i;
        }
        m_len=max(m_len,p[i]-1);
    }
    return m_len;
}
unordered_map<ull, int>mp;
int judge(int len)
{
    mp.clear();
    for(int i=2; i+len-1<tot; i+=2)
    {
        if(p[i]-1>=len)
        {
            int id=i/2-1;
            mp[geths1(id-len/2,id+len/2)]=1;
        }
    }
    for(int i=0; i+len-1<len2; i++)
    {
        if(mp[geths2(i,i+len-1)]==1)
            return 1;
    }
    return 0;
}
int main()
{
    po[0]=1;
    for(int i=1; i<(N<<1); i++)
        po[i]=po[i-1]*31;
    while(~scanf("%s%s",s1,s2))
    {
        len1=len2=tot=0;
        int l1=strlen(s1);
        int l2=strlen(s2);
        s[len1++]='A';
        for(int i=0; i<l1; i++)
            s[len1++]=s1[i],s[len1++]='A';
        ss[len2++]='A';
        for(int i=0; i<l2; i++)
            ss[len2++]=s2[i],ss[len2++]='A';
        str[tot++]='$';
        str[tot++]='#';
        for(int i=0; i<len1; i++)
            str[tot++]=s[i],str[tot++]='#';
        str[tot]='\0';
        hash1[0]=s[0];
        for(int i=1; i<len1; i++)
            hash1[i]=hash1[i-1]*31+s[i];
        hash2[0]=ss[0];
        for(int i=1; i<len2; i++)
            hash2[i]=hash2[i-1]*31+ss[i];
        manacher(tot);
        int l=0,r=N,ans=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(judge(mid<<1|1))
            {
                ans=(mid<<1|1);
                l=mid+1;
            }
            else
                r=mid-1;
        }
        printf("%d\n",ans>>1);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值