manacher算法
manacher算法
斯♥哈♥学长的博客https://www.cnblogs.com/luckyblock/p/17044694.html#5140558
为什么老师叫他马拉车算法/yiw
简介#
我们都知道,求最长回文子串可以枚举每一个开始的点,然后直接一个一个比较就完事,但这样的复杂度是接近 ,那么我们这个时候就需要用到 manacher 算法了,他和 KMP 一样都是基于暴力的思想改进的一种算法,利用了已求得的回文半径加速了比较的过程,使复杂度降到了
过程#
上面已经提到 manacher 算法是解决最长回文子串的问题的。
回文串可以分为两种类型:一种是长度为奇数的回文串,比如
我们定义
这个时候就分两种情况了,第一种情况就是
因为
第二种情况就是
如果在这种情况下,
所以综上,
如果可以的话,继续暴力拓展即可。
复杂度不会证,看上面学长的博客吧。
P3805 【模板】manacher 算法#
#include<bits/stdc++.h>
#define int long long
#define N 20001000
using namespace std;
int p[N<<1],ans,cnt;
char s[N<<1];
signed main()
{
char c=getchar();
s[0]='~';s[++cnt]='%';
while(c<'a'||c>'z')c=getchar();
while(c>='a'&&c<='z')s[++cnt]=c,s[++cnt]='%',c=getchar();
int r=0,mid=0;
for(int t=1;t<=cnt;t++)
{
if(t<=r)p[t]=min(p[mid*2-t],r-t+1);//如果当前的中心是小于r的话,取两头的min
while(s[t-p[t]]==s[t+p[t]])++p[t];//向两边扩展
if(p[t]+t>r)r=p[t]+t-1,mid=t;//如果超界就要更新r和mid的值
ans=max(p[t],ans);//如果要是当前的半径比答案大就替换
}
cout<<ans-1<<endl;
return 0;
}
P4555 [国家集训队]最长双回文串#
打开标签发现有 manacher 算法所以开始考虑 manacher 算法
但是当我求出
既然我们是要求两相邻的回文串的最大长度,那么我们可以考虑维护一下以一个点为开始的回文串最大长度和以一个点为结尾的回文串最大长度,但是这就又有一个问题,就是当前点的字符是被算入了两个串里,考虑一下,我们在一开始插入了一些特殊字符,我们只要枚举这些特殊字符的不就好了吗!因为后面删去是不影响的。然后就是该如何维护,首先我们可以在求
#include<bits/stdc++.h>
#define int long long
#define N 1000100
using namespace std;
int n,p[N<<1],cnt,l[N<<1],r[N<<1],ans;
char s[N<<1];
signed main()
{
char c=getchar();
s[cnt]='~',s[++cnt]='%';
while(c<'a'||c>'z')c=getchar();
while(c>='a'&&c<='z')s[++cnt]=c,s[++cnt]='%',c=getchar();
s[cnt+1]=')';
int j=0,mid=0;
for(int i=1;i<=cnt;i++)
{
if(i<=j)p[i]=min(p[2*mid-i],j-i+1);
while(s[i-p[i]]==s[i+p[i]])p[i]++;
if(p[i]+i>j)j=p[i]+i-1,mid=i;
l[i-p[i]+1]=max(l[i-p[i]+1],p[i]-1);
r[i+p[i]-1]=max(r[i+p[i]-1],p[i]-1);
}
// cout<<"cao"<<endl;
for(int i=cnt;i>=1;i-=2)r[i]=max(r[i],r[i+2]-2);
for(int i=1;i<=cnt;i+=2)l[i]=max(l[i],l[i-2]-2);
for(int i=1;i<=cnt;i+=2)if(l[i]&&r[i])ans=max(l[i]+r[i],ans);
cout<<ans<<endl;
return 0;
}
P1659 [国家集训队]拉拉队排练#
既然是求解回文串的问题,首先可以考虑一下 manacher 算法。
我发现题解中有的是直接在原串上跑 manacher,这样可以直接过滤掉长度为偶数的回文串,如果是要在修改后的串上跑 manacher 的话,我们在统计答案的时候可以直接特判一下,把是以原串里的字符为中心的回文串最大长度给算上,这样在原串里也是长度为奇数的回文串了。
对于排序的问题,我们可以开一个桶
现在考虑如果处理以上的情况。如果是在往桶里面存放的时候用一个循环来一直减二修改的话,像一些以当前点为回文中心的字符串很长的情况就会 T 掉了,所以我们要用到类似于线段树的懒标记的方法,我们在将当前的回文串长度也就是
#include<bits/stdc++.h>
#define int long long
#define P 19930726
#define N 1000010
using namespace std;
int n,k,p[N<<1],t[N<<1],cnt,maxn;
char s[N<<1];
inline int ksm(int x,int y)
{
int sum=1;
while(y)
{
if(y%2==1)
sum=(sum*x)%P;
x=(x*x)%P;
y=y>>1;
}
return sum%P;
}
signed main()
{
cin>>n>>k;
char c=getchar();
s[cnt]='!';s[++cnt]='%';
while(c<'a'||c>'z')c=getchar();
while(c>='a'&&c<='z')s[++cnt]=c,s[++cnt]='%',c=getchar();
s[cnt+1]='~';
int j=0,mid=0;
for(int i=1;i<=cnt;i++)
{
if(i<=j)p[i]=min(p[2*mid-i],j-i+1);
while(s[i+p[i]]==s[i-p[i]])p[i]++;
if(p[i]+i>j)j=p[i]+i-1,mid=i;
if(s[i]!='%')
{
int xx=p[i]-1;
t[xx]++;
maxn=max(maxn,p[i]-1);
}
}
// for(int i=1;i<=maxn;i++)
// cout<<t[i]<<" ";
// cout<<endl;
int ans=1,sum=0;
for(int i=maxn;i>=1;i--)
{
if(sum+t[i]>k)
{
ans=(ans*ksm(i,k-sum)%P);
sum+=t[i];
break;
}
sum+=t[i];
t[i-2]+=t[i];
ans=(ans*ksm(i,t[i])%P);
}
if(sum<k)
{
cout<<"-1"<<endl;
return 0;
}
cout<<ans<<endl;
return 0;
}
P9606 [CERC2019] ABB#
不难发现最坏情况就是把前
发现后缀的回文串长度会使得添加的字符个数减少,例如样例三中 murderforajarof
实际上最后是在后面复制了 redrum
,也就是说我们找到一个最右端为
考虑如何计算。
求最大回文想到 manacher
算法,发现求出
/*
* @Author: Aisaka_Taiga
* @Date: 2023-10-23 15:12:00
* @LastEditTime: 2023-10-23 16:08:55
* @LastEditors: Aisaka_Taiga
* @FilePath: \Desktop\P9606.cpp
* The heart is higher than the sky, and life is thinner than paper.
*/
#include <bits/stdc++.h>
#define int long long
#define N 1000100
using namespace std;
int n, ans, p[N], m;
char s[N], ss[N];
signed main()
{
cin >> n;
for(int i = 1; i <= n; i ++) cin >> ss[i];
s[0] = '~', s[++ m] = '%';
for(int i = 1; i <= n; i ++)
{
s[++ m] = ss[i];
s[++ m] = '%';
}
int r = 0, mid = 0;
for(int i = 1; i <= m; i ++)
{
if(i <= r) p[i] = min(p[2 * mid - i], r - i + 1);
while(s[i + p[i]] == s[i - p[i]]) p[i] ++;
if(p[i] + i > r) r = p[i] + i - 1, mid = i;
}
ans = n - 1;
// for(int i = 1; i <= m; i ++) if(s[i] != '%') cout << (p[i] / 2) << " "; cout << endl;
for(int i = 1; i <= m; i ++)
if(p[i] + i - 1 == m)
ans = min(ans, n - p[i] + 1);
cout << ans << endl;
return 0;
}
作者: 北烛青澜
出处:https://www.cnblogs.com/Multitree/p/17069324.html
本站使用「CC BY 4.0」创作共享协议,转载请在文章明显位置注明作者及出处。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战