[BZOJ]4755: [Jsoi2016]扭动的回文串

Time Limit: 10 Sec  Memory Limit: 512 MB

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

Solution

  一个扭动回文串可以拆成3个部分,第一部分在A,第二部分是A或B的一个回文子串,第三部分在B,其中第一部分翻转后和第三部分相同,并且肯定存在一个最优解,它的第二部分是它回文中心向外延伸得到的最长的回文串,对A和B分别跑manacher找到各个回文中心的最长回文半径后二分+hash即可。

Code

#include<cstdio>
#include<algorithm>
using namespace std;
#define MN 100000
#define X 31
#define MOD 998244353
char a[MN+5],b[MN+5],s[MN*2+5];
int n,p[MN+5],ha[MN+5],hb[MN+5],d[MN*2+5],ans;
void solve(char*S)
{
    int i,mx=0,L,R,l,r,mid,res;
    for(s[0]=‘.‘,i=1;i<2*n;++i)s[i]=i&1?S[i+1>>1]:‘#‘;
    for(i=1;i<2*n;++i)
    {
        d[i]=mx+d[mx]<i?0:min(d[2*mx-i],mx+d[mx]-i);
        while(s[i+d[i]+1]==s[i-d[i]-1])++d[i];
        if(i+d[i]>mx+d[mx])mx=i;
        L=i-d[i]+2>>1;R=i+d[i]+1>>1;
        if(S==a)--R;else ++L;
        for(l=0,r=min(L-1,n-R);l<=r;)
        {
            mid=l+r>>1;
            if((1LL*(ha[L-1]-1LL*ha[L-mid-1]*p[mid])%MOD+MOD)%MOD==
               (1LL*(hb[R+1]-1LL*hb[R+mid+1]*p[mid])%MOD+MOD)%MOD)res=mid,l=mid+1;
            else r=mid-1;
        }
        ans=max(ans,R-L+2+res*2);
    }
}
int main()
{
    int i,mx;
    scanf("%d%s%s",&n,a+1,b+1);
    for(p[0]=i=1;i<=n;++i)p[i]=1LL*p[i-1]*X%MOD;
    for(i=1;i<=n;++i)ha[i]=(1LL*ha[i-1]*X+a[i])%MOD;
    for(i=n;i;--i)hb[i]=(1LL*hb[i+1]*X+b[i])%MOD;
    solve(a);solve(b);
    printf("%d",ans);
}
时间: 2024-12-12 06:50:58

[BZOJ]4755: [Jsoi2016]扭动的回文串的相关文章

[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 第一行

P4324 [JSOI2016]扭动的回文串

传送门 对\(A\).\(B\)串各跑一遍\(manacher\),求出第\(1\).\(2\)类扭动回文串的最大长度. 考虑第三类的扭动回文串\(S(i,j,k)\),一定可以表示为\(A(i,l)+A(l+1,j)+B(j,k)\)或\(A(i,j)+B(j,l)+B(l+1,k)\),其中,第一段与第三段对称(第一段正着\(Hash\)和第三段反着\(Hash\)相同),第二段是一个回文子串,三段都可以是空串. 我们可以分别在\(AB\)上枚举对称中心,然后感性理解一下发现肯定是取以该对称

bzoj 2565: 最长双回文串

Description 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为"abc",逆序为"cba",不相同).输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串. Input 一行由小写英文字母组成的字符串S. Output 一行一个整数,表示最长双回文子串的长度. Sample Input baacaabbacabb Sample Output 12 HINT

BZOJ 3676 【APIO2014】 回文串

题目链接:回文串 我终于也会回文自动机辣! 其实吗--我觉得回文自动机(听说这玩意儿叫\(PAM\))还是比较\(simple\)的--至少比\(SAM\)友善多了-- 所谓回文自动机,每个节点就代表一个回文串.回文自动机的每个节点有两个东西,一个是\(next\),一个是\(fail\).\(next_{u,x}\)指向节点\(u\)所代表的回文串在两端各添加一个字符\(x\)得到一个新的回文串.\(fail_u\)则指向\(u\)这个节点的最长后缀回文串(不包括本身).当然还有一个\(len

BZOJ 2565 最长双回文串 Hash+二分

题目大意:给定一个字符串,求一个最长的子串,该字串可以分解为两个回文子串 傻逼的我又忘了Manacher怎么写了= = 无奈Hash+二分吧 首先将字符串用分隔符倍增,然后求出以每个点为中心的最长回文半径 然后考虑两个回文串怎么合并成一个 我们发现图中以i为中心的回文串和以j为中心的回文串合并后长度恰好为(j-i)*2 能合并的前提是以两个点为中心的回文串有交点 那么对于每个j我们要求出有交点的最左侧的i 维护一个后缀min随便搞搞就可以了 #include <cstdio> #include

loj2073 「JSOI2016」扭动的回文串

ref 主要是要理解"撑到"最长这个概念 (为啥我的代码这么长QAQ #include <iostream> #include <cstdio> using namespace std; typedef unsigned long long ull; int n, pa[200005], pb[200005], ans; ull bse1[200005], bse2[200005], hsa1[200005], hsa2[200005], hsb1[200005

【JSOI2016】扭动的回文串

题面 https://www.luogu.org/problem/P4324 题解 #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define N 100050 #define uLL unsigned long long #define ri register int using namespace std; const uLL p=1233107; i

2565: 最长双回文串 - BZOJ

Description 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为"abc",逆序为"cba",不相同). 输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串.Input 一行由小写英文字母组成的字符串S. Output 一行一个整数,表示最长双回文子串的长度.Sample Input baacaabbacabb Sample Output 12 HINT

【BZOJ】【2565】最长双回文串

Manacher算法 找出一个最长子串S=X+Y,且X和Y都是回文串,求最长的长度是多少…… 同时找两个串明显很难搞啊……但是我们可以先找到所有的回文串!在找回文串的同时我们可以预处理出来l[i]和r[i]分别表示从 i 这个位置开始向左/右最长的回文串有多长,那么我们枚举两个回文串的分割点更新答案即可. 1 /************************************************************** 2 Problem: 2565 3 User: Tunix