Luogu_P3805 【模板】manacher算法

本文详细介绍了Manacher算法,这是一种用于寻找给定字符串中最长回文子串的高效算法,具有O(n)时间复杂度。通过在原字符串两端插入特殊字符,将所有回文子串转换为奇回文,然后利用已知信息动态扩展回文子串,逐步更新最大回文子串的半径。文章还给出了算法的C++实现代码,帮助读者理解并应用Manacher算法。

【模板】manacher算法

题目描述

给出一个只由小写英文字符a,b,c,…y,z\texttt a,\texttt b,\texttt c,\ldots\texttt y,\texttt za,b,c,y,z 组成的字符串SSS,求SSS中最长回文串的长度 。

字符串长度为nnn

输入格式

一行小写英文字符a,b,c,…y,z\texttt a,\texttt b,\texttt c,\ldots\texttt y,\texttt za,b,c,y,z组成的字符串SSS

输出格式

一个整数表示答案。

输入输出样例

输入 #1
aaa
输出 #1
3

说明/提示

1≤n≤1.1×1071\le n\le 1.1\times 10^71n1.1×107

思路

为了在接下来的测试里不被字符串坑,所以不得不来学一下Manacher算法

Manacher算法可以做到在O(n)O(n)O(n)的时间复杂度内实现最大回文子串的查找。

在Manacher算法中,首先要在原字符串的头、尾以及每两个字符的中间插入一个特殊字符。

例如,字符串SSS
a,b,c,b,a,d,a\texttt a,\texttt b,\texttt c,\texttt b,\texttt a,\texttt d,\texttt aa,b,c,b,a,d,a

我们可以按照上述方法,构造出字符串S′S'S
*,a,*,b,*,c,*,b,*,a,*,d,*,a,*,\texttt *,\texttt a,\texttt *,\texttt b,\texttt *,\texttt c,\texttt *,\texttt b,\texttt *,\texttt a,\texttt *,\texttt d,\texttt *,\texttt a,\texttt *,*,a,*,b,*,c,*,b,*,a,*,d,*,a,*,

这样,我们就把所有的回文子串都变成了奇回文(中点坐标是整数的回文子串)。

我们设三个变量r,m,maxnr,m,maxnr,m,maxn,分别表示目前查到的最大回文子串的右端点,目前查到的最大回文子串的中点,目前查到的最大回文子串的半径,再设pip_ipi表示iii为中点的最大回文子串的半径

然后,我们开始扫描整个字符串S′S'S

对于每个字符S′i{S'}_iSi,如果i>ri>ri>r,那么已知的以iii为中点的最大回文子串就只能是iii自己,即pi=1p_i=1pi=1;否则,已知的以iii为中点的最大回文子串的半径要么等于p2∗m−ip_{2*m-i}p2mi(这意味着以iii为中点的最大回文子串中的每个字符S′j{S'}_jSj,满足∀j∈[m,r]\forall j \in [m,r]j[m,r]i∈[m+r2,r]i \in [\frac{m+r}{2},r]i[2m+r,r]),要么等于r−i+1r-i+1ri+1(这意味着以iii为中点的最大回文子串中的每个字符S′j{S'}_jSj,满足∀j∈[m,r]\forall j \in [m,r]j[m,r]i∈[m,m+r2]i \in [m,\frac{m+r}{2}]i[m,2m+r]),在这里,我们取两种情况的最小值,即pi=min(p2∗m−i,r−i+1)p_i=min(p_{2*m-i},r-i+1)pi=min(p2mi,ri+1)

接着,我们对已知的pip_ipi,把它向外扩展,看看能不能构造出半径更长的以它为中点的最大回文子串。因为有了上一步的处理,在这一步中,我们不用向外扩展太多就可以找到半径最长的以它为中点的最大回文子串。这就使得算法的效率极大地提高了!

得到了pip_ipi的最大值之后,我们就可以更新r,c,maxnr,c,maxnr,c,maxn的值了。

最后的答案就是maxn−1maxn-1maxn1。(证明:在字符串S′S'S中,它的最大回文子串的一半的是*,a,*,b,*,c,*\texttt *,\texttt a,\texttt *,\texttt b,\texttt *,\texttt c,\texttt **,a,*,b,*,c,*,显而易见,该子串删去一个*\texttt **之后,在剩余的*\texttt **的位置填上字母,构造的字符串经过排序后就是原字符串SSS的一个子串,而且是SSS的最大回文子串)

代码

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
int cnt,r,m,p[22000001],len,maxn,id;
char ss[22000001];
string s;
int main()
{
	cin>>s;
	len=s.size()*2+1;
	for(int i=1;i<=len;i++)
	{
		if(i&1)
			ss[i]='#';
		else
			ss[i]=s[id++];
	}
	r=m=-1;
	for(int i=1;i<=len;i++)
	{
		if(i>r)
			p[i]=1;
		else
			p[i]=min(p[2*m-i],r-i+1);
		while(i+p[i]<=len&&i-p[i]>=1)
		{
			if(ss[i+p[i]]==ss[i-p[i]])
				p[i]++;
			else
				break;
		}
		if(i+p[i]>r)
		{
			r=i+p[i]-1;
			m=i;
		}
		maxn=max(maxn,p[i]);
	}
	printf("%d",maxn-1);
	return 0;
}
下载方式:https://pan.quark.cn/s/a4b39357ea24 布线问题(分支限界算法)是计算机科学和电子工程领域中一个广为人知的议题,它主要探讨如何在印刷电路板上定位两个节点间最短的连接路径。 在这一议题中,电路板被构建为一个包含 n×m 个方格的矩阵,每个方格能够被界定为可通行或不可通行,其核心任务是定位从初始点到最终点的最短路径。 分支限界算法是处理布线问题的一种常用策略。 该算法与回溯法有相似之处,但存在差异,分支限界法仅需获取满足约束条件的一个最优路径,并按照广度优先或最小成本优先的原则来探索解空间树。 树 T 被构建为子集树或排列树,在探索过程中,每个节点仅被赋予一次成为扩展节点的机会,且会一次性生成其全部子节点。 针对布线问题的解决,队列式分支限界法可以被采用。 从起始位置 a 出发,将其设定为首个扩展节点,并将与该扩展节点相邻且可通行的方格加入至活跃节点队列中,将这些方格标记为 1,即从起始方格 a 到这些方格的距离为 1。 随后,从活跃节点队列中提取队首节点作为下一个扩展节点,并将与当前扩展节点相邻且未标记的方格标记为 2,随后将这些方格存入活跃节点队列。 这一过程将持续进行,直至算法探测到目标方格 b 或活跃节点队列为空。 在实现上述算法时,必须定义一个类 Position 来表征电路板上方格的位置,其成员 row 和 col 分别指示方格所在的行和列。 在方格位置上,布线能够沿右、下、左、上四个方向展开。 这四个方向的移动分别被记为 0、1、2、3。 下述表格中,offset[i].row 和 offset[i].col(i=0,1,2,3)分别提供了沿这四个方向前进 1 步相对于当前方格的相对位移。 在 Java 编程语言中,可以使用二维数组...
源码来自:https://pan.quark.cn/s/a4b39357ea24 在VC++开发过程中,对话框(CDialog)作为典型的用户界面组件,承担着与用户进行信息交互的重要角色。 在VS2008SP1的开发环境中,常常需要满足为对话框配置个性化背景图片的需求,以此来优化用户的操作体验。 本案例将系统性地阐述在CDialog框架下如何达成这一功能。 首先,需要在资源设计工具中构建一个新的对话框资源。 具体操作是在Visual Studio平台中,进入资源视图(Resource View)界面,定位到对话框(Dialog)分支,通过右键选择“插入对话框”(Insert Dialog)选项。 完成对话框内控件的布局设计后,对对话框资源进行保存。 随后,将着手进行背景图片的载入工作。 通常有两种主要的技术路径:1. **运用位图控件(CStatic)**:在对话框界面中嵌入一个CStatic控件,并将其属性设置为BST_OWNERDRAW,从而具备自主控制绘制过程的权限。 在对话框的类定义中,需要重写OnPaint()函数,负责调用图片资源并借助CDC对象将其渲染到对话框表面。 此外,必须合理处理WM_CTLCOLORSTATIC消息,确保背景图片的展示不会受到其他界面元素的干扰。 ```cppvoid CMyDialog::OnPaint(){ CPaintDC dc(this); // 生成设备上下文对象 CBitmap bitmap; bitmap.LoadBitmap(IDC_BITMAP_BACKGROUND); // 获取背景图片资源 CDC memDC; memDC.CreateCompatibleDC(&dc); CBitmap* pOldBitmap = m...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值