[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

HINT

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

.BC..

..ECB

首先我们需要知道扭动的回文串的两种情况

1、它为A串或B串的子串

2、它的对称中心有一部分在A串或B串

对于第一种情况十分好写,这里就不再多说,主要是讲讲第二种情况

对于 第二种情况而言,我们首先枚举回文串中点 i,然后找到最大能扩张的最大范围(i-p[i]~i+p[i]),若回文串的中点在A串,则A串所能继续取到的范围是(1~i-p[i]-1),而B串中所能取到的范围是(i+p[i]~len),若中心点在B串类似。

那么下一步我们该怎么做?二分长度。二分一个长度,然后判断A的一部分和B串的一部分是否一样

如何判断?哈希。记录哈希出来的值的前缀和,B串记录后缀和,判断的时候做类似前缀和的减法即可,记得双哈希

依然不懂?上代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<‘0‘||ch>‘9‘;ch=getchar())  if (ch==‘-‘)    f=-1;
    for (;ch>=‘0‘&&ch<=‘9‘;ch=getchar())    x=(x<<1)+(x<<3)+ch-‘0‘;
    return x*f;
}
inline void print(int x){
    if (x>=10)     print(x/10);
    putchar(x%10+‘0‘);
}
const int N=1e5,limit=27,p1=100007,p2=233333;
char A[N*2+10],B[N*2+10];
int pA[N*2+10],pB[N*2+10];
int sumA[2][N*2+10],sumB[2][N*2+10],g[2][N*2+10];
int len,Ans;
void work(char *s,int *p){
    int Max=0,ID=0;
    for (int i=1;i<=len;i++){
        p[i]=Max>i?min(p[ID*2-i],Max-i):1;
        while (s[i+p[i]]==s[i-p[i]])    p[i]++;
        if (Max<p[i]+i) Max=p[ID=i]+i;
    }
}
bool check(int l1,int r1,int l2,int r2,int Len){  //哈希判断,利用前缀和
    int x=(sumA[0][r1]-1ll*sumA[0][l1-1]*g[0][Len]%p1)%p1;
    int y=(sumB[0][l2]-1ll*sumB[0][r2+1]*g[0][Len]%p1)%p1;
    x=(x+p1)%p1,y=(y+p1)%p1;
    if (x!=y)   return 0;
    x=(sumA[1][r1]-1ll*sumA[1][l1-1]*g[1][Len]%p2)%p2;
    y=(sumB[1][l2]-1ll*sumB[1][r2+1]*g[1][Len]%p2)%p2;
    x=(x+p2)%p2,y=(y+p2)%p2;
    return x==y;
}
int solve(int j,int k){  //二分枚举长度
    int l=0,r=min(j,(len>>1)-k+1),ans=0;
    while (l<=r){
        int mid=(l+r)>>1;
        if (check(j-mid+1,j,k,k+mid-1,mid)) l=mid+1,ans=mid;
        else    r=mid-1;
    }
    return ans;
}
int main(){
    len=read();
    scanf("%s%s",A+1,B+1);
    for (int i=len;i;i--)   A[i<<1]=A[i],B[i<<1]=B[i],A[i<<1|1]=B[i<<1|1]=‘&‘;
    len=len<<1|1;
    A[0]=B[0]=‘#‘,A[1]=B[1]=‘&‘,A[len+1]=B[len+1]=‘^‘,g[0][0]=g[1][0]=1;
    work(A,pA),work(B,pB);
    for (int i=1;i<=len;i++)    pA[i]--,pB[i]--;  //回文串的长度会多出来一位,应该减去
    for (int i=1;i<=len;i++)
        Ans=max(Ans,max(pA[i],pB[i])),   //回文串为子串的情况
        g[0][i]=1ll*g[0][i-1]*limit%p1,
        g[1][i]=1ll*g[1][i-1]*limit%p2;  //记录类似进制一样的东西
    for (int i=2;i<len;i+=2)
        sumA[0][i>>1]=(1ll*sumA[0][(i>>1)-1]*limit+A[i])%p1,
        sumA[1][i>>1]=(1ll*sumA[1][(i>>1)-1]*limit+A[i])%p2;  //记录两个哈希的前缀和,每次多出一位要乘上一个进制(limit)
    for (int i=len-1;i>1;i-=2)
        sumB[0][i>>1]=(1ll*sumB[0][(i>>1)+1]*limit+B[i])%p1,
        sumB[1][i>>1]=(1ll*sumB[1][(i>>1)+1]*limit+B[i])%p2;  //由于对称,所以B串的记录要从后面开始
    for (int i=2;i<len;i++){  //回文串中心在A串中的情况
        int l=i-pA[i],r=i+pA[i];
        l=(l+1)>>1,r>>=1;
        Ans=max(Ans,pA[i]+solve(l-1,r)*2);
    }
    for (int i=2;i<len;i++){  //回文串中心在B串中的情况
        int l=i-pB[i],r=i+pB[i];
        l=(l+1)>>1,r>>=1;
        Ans=max(Ans,pB[i]+solve(l,r+1)*2);
    }
    printf("%d\n",Ans);
    return 0;
}

原文地址:https://www.cnblogs.com/Wolfycz/p/8414484.html

时间: 2024-08-29 14:08:37

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

[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.或者某一个回文的扭动

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\)上枚举对称中心,然后感性理解一下发现肯定是取以该对称

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

最少回文串--牛客网(秋招备战专场三模)-C++方向

题目描述:一个字符串从左向右和从右向左读都完全一样则是回文串,给定一个字符串,问该字符串中的字符所能组成的最少的回文串的个数为多少 解题思路:如果一个字符出现的次数为偶数,则必能组成回文串,如果一个字符出现奇数次,只能自己组成回文串,题目中问最少的回文串数目,即求出现次数为奇数次的字符个数即可,定义a存储每个字符出现的次数,统计出现奇数次的字符的个数,即为输出 1 #include <iostream> 2 #include <string> 3 using namespace s

回文串问题

1.回文串的判断 #include <iostream> #include <string.h> using namespace std; //回文串的判断 bool isPalindrome(const char* src) { if(src == NULL) return true; int end = strlen(src)-1,begin = 0; while(begin < end)//从两边向中间判断,当然也可以从中间向两边判断 { if(src[begin] !

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

BZOJ2565:最长双回文串

2565: 最长双回文串 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2195  Solved: 1119[Submit][Status][Discuss] Description 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为"abc",逆序为"cba",不相同). 输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都

Power oj/2610[判断回文串]

题目链接[https://www.oj.swust.edu.cn/problem/show/2610] 题意:给你一个字符串,让你判断这个字符串是不是回文串,字符串的长度是1<len<1e7,内存是4096KB. 题解:首先这1e7个字符是存不下的,1e71024=9765KB>4096kB.那么怎么办?字符串哈希,先对字符串的前半部分进行哈希,然后在对字符串后半部分进行哈希,如果两部分的哈希值相同,那么这个字符串就是回文串. BKDRH哈希,哈希公式为has=has*seed+s[i]