FJUT-这还是一道数论题

这还是一道数论题

TimeLimit:4000MS  MemoryLimit:128MB
64-bit integer IO format:%lld
Special Judge
 
Problem Description

最后来个字符串签个到吧,这题其实并不难,所需的算法比较基础,甚至你们最近还上过课。

为了降低难度,免得所有人爆零。这里给几个提示的关键字 :字符串,回文,二分,哈希. 注意要对奇偶回文分开二分

这样还不会做,说明基础有所欠缺。

给你一个字符串A和一个字符串B,请你求一个满足以下要求的所有字符串中,最长的字符串C的长度:

  1. C必须同时是A和B的子串,即A和B中都必须存在一个子区间和C长得一样

  2. C必须是一个回文,即正过来读和反过来读都一样

Input

多组数据,请处理到EOF

每组数据包含,两行,每行都是仅由小写字符构成的字符串,代表A和B。

对于30%的数据。

保证|A|,|B|<=1000,且单个文件的字符总数小于10000

对于100%的数据

保证|A|,|B|<=100000,且单个文件的字符总数小于2e6

其中70%的数据答案为奇数哦

因为没有处理掉字符串尾巴上多余的' ',所以为了防止读到' ' 推荐使用scanf("%s");

Output

输出C的长度

SampleInput
bbabbbcbcbbbddde
bbbabbbccadddbbba
dggdgzz
dggdbb
aaa
bbb
acbdbca
bcadacb
SampleOutput
5
4
0
1
hint:
对于第一个数据C取bbabb
对于第二个数据C取dggd



用马拉车预处理,这样可以O(1)判断任意区间是否是回文。 然后就是用二分答案的长度,用哈希O(n)计算是否有长度为len的公
共回文子串,注意。  二分时奇偶回文要分开二分。因为如何有2k+2长度的公共回文。肯定也会有2k长度的公共回文,但是不保证也
有2k+1的长度的公共回文。。数据里70%的答案是奇的。所以你只二分奇数长度即可得到70分. 如果不想奇偶分开讨论的话,可以用马拉车的技巧,转化为全部都是奇回文
细节在代码中有解释
#include<bits/stdc++.h>
typedef unsigned long long ull;
const int maxn=100005;
const ull P=131;
using namespace std;
char str1[maxn],str2[maxn];
ull f1[maxn],f2[maxn],p[maxn];
int len1,len2,tmp_len;
int len[maxn<<1];
char tmp[maxn<<1];
void init()
{
    p[0]=1;
    for(int i=1; i<maxn; i++)
    {
        p[i]=p[i-1]*P;
    }
}
ull Hash(ull f[],int l,int r)
{
    return f[r+1]-f[l]*p[r-l+1];
}

void GetHash(char str[],ull f[])
{
    int len_str=strlen(str);
    f[0]=0;
    for(int i=0; i<len_str; i++)
        f[i+1]=f[i]*P+str[i];
}
int manachar_init(char st[])
{
    int i,str_len=strlen(st);
    tmp[0]='@';///加一个特殊字符,防止越界
    for(int i = 1; i <= 2*str_len ; i+=2)
    {
        tmp[i]='#';
        tmp[i+1]=st[i/2];
    }
    tmp[2*str_len+1]='#';
    tmp[2*str_len+2]='$';///字符串结尾加一个字符,防止越界
    return 2*str_len+1;///返回转换字符串的长度
}
void manachar(char st[],int str_len)
{
    int p_mx=0,ans=0,po=0;///p_mx即为“当前”计算回文串最右边字符的最大值,po为当前该回文字符串的中心
    for(int i=1;i<=str_len;i++)
    {
        if(i<p_mx)
            len[i]=min(p_mx-i,len[2*po-i]);///po对应最大回文子串中心点的位置
        else
            len[i]=1;
            while(st[i-len[i]]==st[i+len[i]])
                len[i]++;
            if(len[i]+i>p_mx)
            {
                po=i;
                p_mx=i+len[i];
            }
        ans=max(ans,len[i]);
    }
}
map< ull,int >mp;
bool cheak(int m)
{
    mp.clear();
    for(int i=0; str1[i+m-1]; i++)
    {
        if(len[i+i+m+1]-1>=m)///i+i+m+1对应原回文子字符串str[i~(i+m-1)]在转换后字符串的回文中心位置(l+r+2)
        {
            ull cnt=Hash(f1,i,i+m-1);
            mp[cnt]=i;///记录该回文串的起点
        }
    }
    for(int i=0; str2[i+m-1]; i++) ///枚举第二个字符串的起点
    {
        ull cnt=Hash(f2,i,i+m-1);
        if(mp.count(cnt))
        {
            int po=mp[cnt];///po为在str1中的起点
            int j=0;
            for( j=0; j<m; j++)///判断这两个字符串是否相等
            {
                if(str1[po+j]!=str2[i+j])
                    break;
            }
            if(j>=m)
                return true;
        }
    }
    return false;
}
int main()
{
    init();
    while(scanf("%s %s",str1,str2)!=EOF)
    {
        int ans=0;
        GetHash(str1,f1);
        GetHash(str2,f2);
        len1=strlen(str1);
        len2=strlen(str2);
        tmp_len=manachar_init(str1);
        ///tmp为转换后的字符串 tmp_len该字符串长度
        manachar(tmp,tmp_len);
        int l=0,r=min(len1,len2)/2+1;
        while(l+1<r)
        {
            int mid=(l+r)/2;
            if(cheak(mid*2))
            {
                l=mid;
            }
            else
            {
                r=mid;
            }
        }
        ans=max(ans,l*2);
        l=l-1,r=min(len1,len2)/2+1;
        while(l+1<r)
        {
            int mid=(l+r)/2;
            if(cheak(mid*2+1))
            {
                l=mid;
            }
            else
            {
                r=mid;
            }
        }
        ans=max(ans,l*2+1);
        printf("%d
",ans);
    }
    return 0;
}

  



原文地址:https://www.cnblogs.com/yuanlinghao/p/10747034.html