BZOJ4755 [Jsoi2016]扭动的回文串
2018-02-17 16:34
411 查看
标签:Manacher,hash,二分
一个“扭动字符串S(i,j,k)由A中的第i个字符到第j个字符组成的子串与B中的第j个字符到第k个字符组成的子串拼接而成。
比如,若A=’XYZ’,B=’UVW’,则扭动字符串S(1,2,3)=’XYVW’。
JYY定义一个“扭动的回文串”为如下情况中的一个:
A中的一个回文串;
B中的一个回文串;
或者某一个回文的扭动字符串S(i,j,k)
现在JYY希望找出最长的扭动回文串。
第二行包含一个长度为N的由大写字母组成的字符串A。
第三行包含一个长度为N的由大写字母组成的字符串B。
1≤N≤10^5。
定义一个“扭动的回文串”为如下情况中的一个:
A中的一个回文串;
B中的一个回文串;
或者某一个回文的扭动字符串S(i,j,k)
对于第三种情况,枚举这个扭动字符串S的回文中心x
如果x位于A中,那么A能取到的左边界为1->x-pa[x]-1,B能取到的右边界为x+pb[x]->len
位于B中同理
下面可以二分长度判断A的一部分是否和B的一样,判断的过程用hash+前后缀和
常数太丑接近垫底了
题目
题目传送门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定义一个“扭动的回文串”为如下情况中的一个:
A中的一个回文串;
B中的一个回文串;
或者某一个回文的扭动字符串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
题意
给定两个字符串,求它们的最长扭动回文串定义一个“扭动的回文串”为如下情况中的一个:
A中的一个回文串;
B中的一个回文串;
或者某一个回文的扭动字符串S(i,j,k)
分析
前两种情况很好求,不多说对于第三种情况,枚举这个扭动字符串S的回文中心x
如果x位于A中,那么A能取到的左边界为1->x-pa[x]-1,B能取到的右边界为x+pb[x]->len
位于B中同理
下面可以二分长度判断A的一部分是否和B的一样,判断的过程用hash+前后缀和
常数太丑接近垫底了
code
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,a,b) for(register int i=a;i<=b;i++) #define dep(i,a,b) for(register int i=a;i>=b;i--) #define ll long long #define mem(x,num) memset(x,num,sizeof x) #define reg(x) for(int i=last[x];i;i=e[i].next) using namespace std; inline ll read() { ll f=1,x=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn=1e5+6,limit=17,p1=1e5+7,p2=1e5+9; char a[maxn<<1],b[maxn<<1]; int pa[maxn<<1],pb[maxn<<1],sa[2][maxn<<1],sb[2][maxn<<1],g[2][maxn<<1],len,ans; void Manacher(char *ch,int *p){ int Max=0,id; rep(i,1,len){ if(Max>i)p[i]=min(Max-i,p[2*id-i]);else p[i]=1; while(ch[i+p[i]]==ch[i-p[i]])p[i]++; if(Max<p[i]+i)Max=p[id=i]+i; } } inline bool check(int l1,int r1,int l2,int r2,int Len){ int x=(sa[0][r1]-1ll*sa[0][l1-1]*g[0][Len]%p1)%p1; int y=(sb[0][l2]-1ll*sb[0][r2+1]*g[0][Len]%p1)%p1; x=(x+p1)%p1,y=(y+p1)%p1; if(x!=y)return 0; x=(sa[1][r1]-1ll*sa[1][l1-1]*g[1][Len]%p2)%p2; y=(sb[1][l2]-1ll*sb[1][r2+1]*g[1][Len]%p2)%p2; x=(x+p2)%p2,y=(y+p2)%p2; return x==y; } inline int solve(int L,int R){ int l=0,r=min(L,(len>>1)-R+1),ans=0; while(l<=r){ int mid=(l+r)>>1; if(check(L-mid+1,L,R,R+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); dep(i,len,1)a[i<<1]=a[i],b[i<<1]=b[i],a[i<<1|1]=b[i<<1|1]='&'; deb8 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; Manacher(a,pa),Manacher(b,pb);rep(i,1,len)pa[i]--,pb[i]--; rep(i,1,len){ 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){ sa[0][i>>1]=(1ll*sa[0][(i>>1)-1]*limit+a[i])%p1; sa[1][i>>1]=(1ll*sa[1][(i>>1)-1]*limit+a[i])%p2; } for(int i=len-1;i>1;i-=2){ sb[0][i>>1]=(1ll*sb[0][(i>>1)+1]*limit+b[i])%p1; sb[1][i>>1]=(1ll*sb[1][(i>>1)+1]*limit+b[i])%p2; } rep(i,2,len-1){ 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); } rep(i,2,len-1){ 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); } cout<<ans<<endl; return 0; }
相关文章推荐
- BZOJ4755: [JSOI2016]扭动的回文串——题解
- BZOJ4755 [Jsoi2016]扭动的回文串
- [bzoj4755][JSOI2016]扭动的回文串
- BZOJ4755 [JSOI2016]扭动的回文串 【后缀数组】【manacher】
- [BZOJ]4755: [Jsoi2016]扭动的回文串
- [Jsoi2016]扭动的回文串
- bzoj4755: [Jsoi2016]扭动的回文串 manacher+二分+Hash
- [bzoj4755][Jsoi2016]扭动的回文串
- bzoj 4753: [Jsoi2016]最佳团体 二分答案+树形dp
- BZOJ4855 : [Jsoi2016]轻重路径
- 蘑菇街2016研发工程师编程题--回文串
- [编程题] 回文串 java 蘑菇街2016研发工程师在线编程题
- 【LOJ】#2073. 「JSOI2016」扭动的回文串
- BZOJ4753 JSOI2016最佳团体(分数规划+树形dp)
- [JSOI2016]反质数序列
- 【JSOI2016】最佳团队
- [bzoj4854][Jsoi2016]无界单词【dp】
- hdu 5744 Keep On Movin(2016 Multi-University Training Contest 2——回文串,思维题)
- 2016腾讯笔试题,删除字符串中的字符,使得剩下的字符串是回文串,如何删除使得回文串最长
- bzoj4754[JSOI2016]独特的树叶