bzoj4755: [Jsoi2016]扭动的回文串 manacher+二分+Hash

bzoj4755: [Jsoi2016]扭动的回文串

Description

JYY有两个长度均为N的字符串A和B。
一个“扭动字符串S(i,j,k)由A中的第i个字符到第j个字符组成的子串
与B中的第j个字符到第k个字符组成的子串拼接而成。
比如,若A=’XYZ’,B=’UVW’,则扭动字符串S(1,2,3)=’XYVW’。
JYY定义一个“扭动的回文串”为如下情况中的一个:
1.A中的一个回文串;
2.B中的一个回文串;
3.或者某一个回文的扭动字符串S(i,j,k)
现在JYY希望找出最长的扭动回文串。

Input

第一行包含一个正整数N。
第二行包含一个长度为N的由大写字母组成的字符串A。
第三行包含一个长度为N的由大写字母组成的字符串B。
1≤N≤10^5。

Output

输出的第一行一个整数,表示最长的扭动回文串。

Sample Input

5
ABCDE
BAECB

Sample Output

5
最佳方案中的扭动回文串如下所示(不在回文串中的字符用.表示):
.BC..
..ECB

分析

设扭动的回文串为 M(i,j) M ( i , j ) ,则
M(i,j)=Sa(i,k)+Paorb(k,l)+Sb(l,j) M ( i , j ) = S a ( i , k ) + P a o r b ( k , l ) + S b ( l , j )
其中 Paorb(k,l) P a o r b ( k , l ) 是关于某个中心点的最长的回文串, (k,l) ( k , l ) 区间的开闭由其是在A串还是B串中决定。 Sa(i,k),Sb(l,j) S a ( i , k ) , S b ( l , j ) 是反对称的。
然后对于A和B中的每个对称中心,我们manacher一遍求出 Paorb(k,l) P a o r b ( k , l ) ,两边二分+Hash即可。

代码

/**************************************************************
    Problem: 4755
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:704 ms
    Memory:4808 kb
****************************************************************/

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1e5 + 10;
unsigned long long se = 31;
char s[N << 1], a[N], b[N];
int n, f[N << 1], ans;
unsigned long long ha[N], hb[N], bin[N];
void Solve(char *S) {
    s[0] = '.'; int id = 0, tot = 0;
    for(int i = 1;i <= n; ++i) {
        s[++tot] = S[i];
        if(i != n) s[++tot] = '#';
    }
    for(int i = 1;i <= tot; ++i) {
        if(i < id + f[id]) f[i] = min(id + f[id] - i, f[(id << 1) - i]);
        else f[i] = 0;
        while(s[i - f[i] - 1] == s[i + f[i] + 1]) ++f[i];
        if(i + f[i] > id + f[id]) id = i;
        int L = i - f[i] + 2 >> 1, R = i + f[i] + 1 >> 1;
        if(S == a) --R; else ++L; int res = 0;
        for(int l = 0, r = min(L - 1, n - R); l <= r; ) {
            int mid = l + r >> 1;
            if(ha[L - 1] - ha[L - mid - 1] * bin[mid] == hb[R + 1] - hb[R + mid + 1] * bin[mid])
                res = mid, l = mid + 1;
            else r = mid - 1;
        }
        ans = max(ans, R - L + 2 + (res << 1));
    }
}

int main() {
    scanf("%d%s%s", &n, a + 1, b + 1);
    bin[0] = 1; for(int i = 1;i <= n; ++i) bin[i] = bin[i - 1] * se;
    for(int i = 1;i <= n; ++i) ha[i] = ha[i - 1] * se + a[i];
    for(int i = n; i; --i) hb[i] = hb[i + 1] * se + b[i];
    Solve(a); Solve(b);
    printf("%d\n", ans);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值