“一切都会好起来的。”|

zplqwq

园龄:3年10个月粉丝:25关注:14

2022-06-21 17:31阅读: 68评论: 0推荐: 0

题解 P4324 [JSOI2016]扭动的回文串

前置知识:

  • 马拉车

  • hash+二分求最长回文

首先,第一种贺第二种情况马拉车可以直接解决,在此不再赘述。

我们考虑第三种情况。

此时,qwqqaq 必然是相等的,最后答案肯定是 qwq 向上扩展到 A串,然后 qaq 向右扩展。

Q:我们如何判断是否该向 A 串扩展了呢。

A:在B串中的回文串到头了就该连A了。举个例子,在这幅图中,假设以我画的那个点为回文中心,到 qwq 的边界就到头了。这个时候考虑将 qwq 向 A 串扩展,而 qaq 则向右扩展匹配回文。

因此,我们需要确定当前回文串最多可以扩展到哪里。然后从这个地方开始二分最多能扩展的长度。如果满足题意,计入答案。

但是,这只是其中一种情况。因为此时的回文中心可以在 A串,也可以在 B串。而且此时的回文串可以是奇回文,也可以是偶回文。

代码非常恶心,我们一部分一部分来。

第一部分:马拉车求前两种情况。

int init(char st[])
{
int len=strlen(st);
int j=2;
s[0]='^';
s[1]='#';
for (int i=0;i<len;i++)
{
s[j++]=st[i];
s[j++]='#';
}
s[j]='$';
return j;
}
int manacher(char st[])
{
int len=init(st);
int mx=1;
int k=1;
int ans=-1;
for(int i=1;i<len;i++)
{
if(i<mx) pp[i]=min(pp[2*k-i],mx-i);
else pp[i]=1;
while(s[pp[i]+i]==s[i-pp[i]]) pp[i]++;
if(mx<i+pp[i])
{
mx=i+pp[i];
k=i;
}
ans=max(ans,pp[i]-1);
}
return ans;
}

这个如果不会的话建议回去学马拉车。

第二部分:哈希

for(int i=1;i<=n;i++)
{
h1[i]=h1[i-1]*rui+a[i];
h2[i]=h2[i-1]*rui+b[i];
p[i]=p[i-1]*rui;
}
for(int i=n;i>=1;i--)
{
hh[i]=hh[i+1]*rui+a[i];
// cout<<hh[i]<<endl;
hh2[i]=hh2[i+1]*rui+b[i];
}

这个不会的话建议重学字符串哈希。

第三部分:处理奇回文与偶回文的长度

这里需要二分答案。

我们枚举回文中心,然后对于每一个回文中心二分长度。

至于判定,只需要判定两边端点的 hash 值是否相等即可。

nt check(int now,int mid)
{
if(h1[now]-h1[now-mid]*p[mid]==hh[now]-hh[now+mid]*p[mid]) return 1;
else return 0;
}
int check2(int now,int mid)
{
if(h1[now]-h1[now-mid]*p[mid]==hh[now+1]-hh[now+1+mid]*p[mid]) return 1;
else return 0;
}
int check3(int now,int mid)
{
if(h2[now]-h2[now-mid]*p[mid]==hh2[now]-hh2[now+mid]*p[mid]) return 1;
else return 0;
}
int check4(int now,int mid)
{
if(h2[now]-h2[now-mid]*p[mid]==hh2[now+1]-hh2[now+1+mid]*p[mid]) return 1;
else return 0;
}
int check5(int nowl,int nowr,int mid)
{
if(h1[nowl]-h1[nowl-mid]*p[mid]==hh2[nowr]-hh2[nowr+mid]*p[mid]) return 1;
return 0;
}
int bs(int l,int r,int now)
{
int mid;
int best;
while(l<=r)
{
mid=(l+r)/2;
if(check(now,mid))
{
l=mid+1;
best=mid;
}
else r=mid-1;
}
return best;
}
int bs2(int l,int r,int now)
{
int mid;
int best;
while(l<=r)
{
mid=(l+r)/2;
if(check2(now,mid))
{
l=mid+1;
best=mid;
}
else r=mid-1;
}
return best;
}
int bss(int l,int r,int now)
{
int mid;
int best;
while(l<=r)
{
mid=(l+r)/2;
if(check3(now,mid))
{
l=mid+1;
best=mid;
}
else r=mid-1;
}
return best;
}
int bss2(int l,int r,int now)
{
int mid;
int best;
while(l<=r)
{
mid=(l+r)/2;
if(check4(now,mid))
{
l=mid+1;
best=mid;
}
else r=mid-1;
}
return best;
}

第四部分:确定端点,二分最大扩展长度。

首先,二分最大扩展长度其实是与前面的部分差不多的。因为我们只需要不停向外扩展,对于每一次扩展,判断是否回文即可。

当我们确定了最大扩展长度,与原回文串相加即可。

int check5(int nowl,int nowr,int mid)
{
if(h1[nowl]-h1[nowl-mid]*p[mid]==hh2[nowr]-hh2[nowr+mid]*p[mid]) return 1;
return 0;
}
int bss4(int l,int r,int nowl,int nowr)
{
int mid;
int best;
while(l<=r)
{
mid=(l+r)/2;
if(check5(nowl,nowr,mid))
{
l=mid+1;
best=mid;
}
else r=mid-1;
}
return best;
}

至此,这道题已经愉快的被你切了。

本文作者:zplqwq

本文链接:https://www.cnblogs.com/zplqwq/p/16397665.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   zplqwq  阅读(68)  评论(0编辑  收藏  举报
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示