马拉车(manacher)

本文介绍了一种在O(n)时间内找到最长回文串的高效算法——Manacher算法。通过改造字符串,定义回文半径,并利用加速盒子的概念,实现快速求解。文中详细解释了算法流程,包括字符串改造、回文半径的定义、加速盒子的使用等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作用:在O(n)时间内找到最长回文串

1.改造字符串:

字符串有奇偶之分,但偶回文串不好找对称中心,所以用此方法将字符串变为奇字符串。

方法:在字符之间和两边插入 ‘#’

2.回文半径:

定义:以i为中心,最长回文串长度的一半

例如:
在这里插入图片描述
此时以 i i i为中心的回文串长度即为 p [ i ] − 1 p[i]-1 p[i]1

3.加速盒子:

盒子范围: [ l , r ] [l,r] [l,r]
维护右端点最靠右的最长回文串,利用数组p[i]对称点的值转移,从而节省时间。
例如:
在这里插入图片描述
在枚举完前 i − 1 i-1 i1个字符后,维护盒子 [ l , r ] [l,r] [l,r]
流程如下:
盒内:
i ≤ r i\le r ir, i i i的对称点为 r − i + l r-i+l ri+l,
若对称点的回文半径均在盒内,即 p [ r − i + l ] < r − i + 1 , p [ i ] = p [ i − l + 1 ] p[r-i+l]<r-i+1,p[i]=p[i-l+1] p[ri+l]<ri+1,p[i]=p[il+1]
否则,此时在盒内的部分可以先赋值为 d [ i ] = r − i + 1 d[i]=r-i+1 d[i]=ri+1,然后从 r + 1 r+1 r+1开始暴力查看在盒外的部分有无以 i i i为中心点的对称字符
盒外:
否则,从 i i i开始暴力
如果 i + p [ i ] − 1 > r i+p[i]-1>r i+p[i]1>r,更新盒子 l = i − p [ i ] + 1 , r = i + p [ i ] − 1 l=i-p[i]+1,r=i+p[i]-1 l=ip[i]+1,r=i+p[i]1

题目:

模板题

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
//const ull mod=2e64; 
const int N = 2e7+2e6+20;
//const int p=131;
int p[N];//存字符的回文半径
string ss;//s:原字符串;ss:改变后的字符串
void add(string s){
	ss+='$';
	for(int i=0;i<s.size();i++){
		ss+='#';
		ss+=s[i];
	}
	ss+='#';
} 
void manacher(){
	p[1]=1;
	for(int i=2,l,r=1;i<ss.size();i++){
		if(i<=r)p[i]=min(p[r-i+l],r-i+1);//盒内两种情况 
		while(ss[i-p[i]]==ss[i+p[i]])p[i]++;//暴力,中心拓展 
		if(i+p[i]-1>r)l=i-p[i]+1,r=i+p[i]-1;//更新盒子左右边界 
	}
}
 
int main(){
	string s;
	cin>>s;
	add(s);
	manacher();
	int ans=0;
	for(int i=1;i<ss.size();i++){
		ans=max(ans,p[i]);
	}
	cout<<ans-1;
	return 0;
}
### 实现与解释拉车算法 #### 一、算法概述 拉车算法(Manacher's Algorithm)用于在线性时间内查找字符串中的最长回文子串。该算法通过巧妙地处理奇偶长度的回文问题,使得整个过程可以在 O(n) 时间复杂度下完成[^1]。 #### 二、算法过程分析 ##### 1. 字符串预处理 为了统一处理不同长度的回文情况,通常会在原字符串之间插入特殊字符 `#` ,并添加头尾两个不同的保护字符 `$` 和 `@` 。这样做的目的是让所有的回文中心都有一个明确的位置,并且不会越界访问[^2]。 例如:"abba" 被转换成 "$#a#b#b#a#@" ##### 2. 原字符串与新字符串的关联 经过上述变换后的字符串称为辅助串 S' , 对应位置 i 处的最大半径 P[i] 表示以第i个字符为中心能扩展出去最远距离的一半加一 (即实际回文长度除以2再加1)[^3]。 ##### 3. 利用已知信息加速计算 在遍历过程中维护三个变量:当前最大右端点 id, 当前最大右侧边界 mr 及其对应的中心 pos;当遇到新的待求解点 j 时分两种情况进行讨论: ###### 3.1 维护数据 如果j位于mr之内,则尝试借用pos处的信息来减少不必要的比较次数;否则直接从零开始匹配直到无法继续为止。 ###### 3.2 初始化规则 根据 j 是否处于已有记录的有效区间内采取不同策略初始化 p[j]: - **若 j 属于有效区域** :设 k=2*pos-j 即关于pos对称的那个索引位上的值作为初始估计值 min(p[k], mr-j+1), 这样既考虑到了镜像关系又防止超出范围; - **反之则置为默认最小值1**, 因为我们至少知道它自己本身构成的一个单字符回文. ###### 3.3 探测阶段 无论哪种情形都需要进一步验证是否存在更长的可能性,具体做法是从p[j]-1往两侧逐次试探直至不满足条件停止更新p[j][^3]。 最后每当发现更大的mr就及时刷新id和pos以便后续节点能够受益于此优化措施. ```python def manacher(s): # Preprocess the string to handle even-length palindromes uniformly. t = '#'.join(f'^{s}$') n = len(t) p = [0]*n center = right = 0 max_len = 0 index = 0 for i in range(1,n-1): mirror = 2 * center - i if right > i: p[i] = min(right-i,p[mirror]) while t[i+(1+p[i])] == t[i-(1+p[i])]: p[i]+=1 if i + p[i]>right: center=i right=center+p[i] if p[i]>max_len: max_len=p[i] index=i start=(index-max_len)//2 return s[start:start+max_len] ``` 此代码实现了完整的拉车算法逻辑,包括必要的预处理步骤以及核心循环结构,最终返回输入字符串中最长连续回文子序列的内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值