Newcoder 58 C.最长回文(Manacher+二分+hash)

Description

有两个长度均为 n n n的字符串 A A A B B B。可以从 A A A中选一个可以为空的子串 A [ l 1 . . r 1 ] A[l_1..r_1] A[l1..r1] B B B中选一个可以为空的子串 B [ l 2 . . r 2 ] B[l_2..r_2] B[l2..r2],满足 r 1 = l 2 r_1=l_2 r1=l2,然后把它们拼起来 ( A [ l 1 . . r 1 ] + B [ l 2 . . r 2 ] ) (A[l_1..r_1]+B[l_2..r_2]) A[l1..r1]+B[l2..r2]。求用这样的方法能得到的最长回文串的长度。注意:求的不是本质不同的回文串个数哦!!!

Input

第一行一个数 n n n

第二行表示字符串 A A A

第三行表示字符串 B B B

( 1 ≤ n ≤ 1 0 5 ) (1\le n\le 10^5) (1n105)

Output

输出一行一个数表示答案

Sample Input

5
ZQZFC
NSZXL

Sample Output

3

Solution

一个显然的结论是,所选子串必然至少有一个是原串中的极大回文串(或者说答案必然可以写成这样的形式),不妨认为是 A A A串的一个极大回文串拼上 A A A串左端一个子串和 B B B串一个子串构成,那么后两部分需要回文,将 B B B串翻转后这两部分需要相同,首先对 A A A串跑一遍 M a n a c h e r Manacher Manacher,然后二分两端拼接的长度,把 A A A B B B的反串 h a s h hash hash以快速判断两个子串是否相同

Code

#include
#include
using namespace std;
typedef unsigned long long ull;
const int maxn=200005;
char A[maxn],B[maxn],s[maxn];
int len[maxn];
void Manacher(char *s,int n)
{
    len[0]=1;
    int mx=0,id=0;
    s[n]='#';
    for(int i=1;ii?min(len[2*id-i],mx-i):1;
        while(i-len[i]>=0&&s[i+len[i]]==s[i-len[i]])len[i]++;
        if(i+len[i]>mx)
        {
            mx=i+len[i];
            id=i;
        }
    }
    for(int i=0;i=0;i--)A[2*i]=s[i];
    scanf("%s",s);
    for(int i=n-1;i>=0;i--)B[2*i]=s[i];
    n=2*n-1;
    b[0]=1;
    for(int i=1;i<=n;i++)b[i]=b[i-1]*base;
    int ans=Solve(A,B);
    reverse(A,A+n);
    ans=max(ans,Solve(B,A));
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(Newcoder,二分三分,Manacher,hash)