如何求最长回文子串

如何求最长回文子串回文字符串,就是像“12321”这种轴对称形式的字符串,系不系很简单呀(狗头)。但并不是所有的字符串都是这种整个串都是回文串的。有些计算机问题就是在一个字符串中找出一段最长的回文字符子串,这个时候时候,我们会很自然的想到一种暴力的方法来解决。1975年,一位叫Manacher的人发明了一个算法,这个算法是用来查找一个字符串的最长回文子串的方法。…

大家好,又见面了,我是你们的朋友全栈君。

前言

回文字符串,就是像“12321”这种轴对称形式的字符串。
但并不是所有的字符串都是这种整个串都是回文串的,比如1232。有些计算机问题就是在一个字符串中找出一段最长的回文字符子串,这个时候时候,我们就需要一些算法来求出结构。

方法一:暴力求解
char str[10]={ 
   "5234331"};
	int len=strlen(str),max=0;
	for(int i=0;i<len;i++){ 
   
		int res=1,j=1;
		while(i-j>=0&&i+j<len-1&&str[i-j]==str[i+j]) { 
   res+=2;j++;}
		if(res>max) max=res;
	}
	printf("%d",max);

这种做法就是对每一个字符,匹配它左右两边的字符,如果相同,则res+=2,最后取最大值max。
时间复杂度达到了O(n2),当字符长度到10000时,程序的效率就不行了,时间复杂度太高。
而且这种方法还要判断长度的奇偶性(上面只是判断奇数,在这里就不多说了),代码比较长。

方法二:Manacher算法

时间复杂度由O(n2)缩短为O(n),运行效率提高了很多(tql)。
1. 变换
既然回文字符串有奇偶之分,分奇偶的话,程序将会很复杂,那么我们就要想办法避免这种情况。随便选两个不同的字符串,比如”123324″,“123432”,这两个字符串的最长回文子串奇偶性都不同。那么我们选一个字符串中没出现的字符(如#),将其插入到上面的字符串每个字符的左右两边,变成如下形式
#1#2#3#3#2#4#
#1#2#3#2#3#2#
这样回文子串长度都变成了奇数,有利于计算(舒服)。
处理一个字符串S之后,我们在利用一个和S等长的数组L,其中L[ i ]表示以S[ i ]为中心的回文串的半径,如下:
212321
# 2 # 1 # 2 # 3 # 2 # 1 #
1 2 1 4 1 2 1 6 1 4 1 2 1

仔细观察上面的式子,发现以‘3’为中心的回文串半径为6,而对应的回文串“12321”长度为5,以‘1’为中心的回文串的半径为4,而对应的回文串“212”长度为3,规律很快就找到了:回文串的长度等于半径-1。所以我们只需要找出最大的半径就可以找出最长的回文串的长度。但是如果想要定位最长回文子串的位置,我们还需要知道字符串的起始位置。
我们来看“12321”这个回文子串,它的中间字符‘3’在改变后的字符串中的位置为7,它的半径为6,7-6=1,这样发现,字符串“12321”在原字符串中的位置就是1。再来看“212”这个字符串,他的中间字符位置和半径分别为3和4,但是3-4=-1,起始位置变成负数了,这样是不行的。所以我们这时在字符串的首位添加一个特殊的字符。
比如‘&’,字符串变成:
& # 2 # 1 # 2 # 3 # 2 # 1 #
1 1 2 1 4 1 2 1 6 1 4 1 2 1

(下标从零开始)
这样做仅仅只是把字符串往后移一位,没有做出过多改变,但是带来的收益却很大。
再来看“212”这个字符串,它的中间字符位置变为,4,半径为4,这样一减等于0,没有出现负数,而且在原字符串中的起始位置也为0,感觉可以,再来看“12321”,它的中间字符位置变为8,半径为6,一减等于2,除以2等于1,它在原字符串中的起始位置也为1,这样和上面的例子结合起来,发现添加‘&’后:
( 中间字符的位置-半径 ) / 2=在原字符串中的起始位置
由上面的推导,我们得出算法的规律,现在就差代码实现了。
2. 计算
现在需要的就是如何求出半径数组L[ i ]。设idmx分别为最接近字符尾的回文子串的中点位置和右端位置。那么整个核心算法如下:
L[i]=mx>i?min(L[2*id-i],mx-i):1;
当mx>i,则L[ i ]=min( L[2 * id – i] , mx-i),否则L[ i ]=1。
在这里插入图片描述如上图:

  1. 当mx-i>L[ j ]的时候,以S[ id ]为中心的回文子串包含以S[ j ]为中心的回文子串,由于 i 和 j 对称且id左右两边对称,所以以S[ id ]为中心的回文子串必然也包含以S[ j ]为中心的回文子串,见上图,所以有L[ i ]=L[ j ],其中 j = id * 2-i 。
    在这里插入图片描述
  2. 当mx-i<L[ j ]的时候,以S[ id ]为中心的回文子串不一定完全包含以S[ j ]为中心的回文子串,但由于对称性可知,L[ i ]和L[ j ]在绿线以内的部分是相同的,但是到mx之外的部分需要额外取匹配
  3. 当i>mx时,因为i已经超过mx了,所以找不到对称点,只能额外匹配了。
    通过上面的过程可以得出一个最长回文子串,下面给出代码
#include<bits/stdc++.h>
using namespace std;
int main(){ 
   
	string s,t("!#");
	getline(cin,s);
	//将s化成 !#a#b#c#b#c 这种形式
	//其目的是让奇数和偶数的情况相同
	//方便下面的最长回文串的计算 
	for(int i=0;i<s.size();i++){ 
   
		t+=s[i];
		t+='#';
	}
	vector<int> p(t.size(),0);
	int mx=0,id=0,resl=0,resc=0;
	//mx为回文串最右端
	//id为能达到最右端的回文串的中间 
	//resl为最大的回文串的半径
	//resc为最长的回文串的中间位置 
	for(int i=1;i<t.size();i++){ 
   
		//马拉车的核心算法 //判断以t[i]为中心的回文串长度 
		p[i]=mx>i?min(p[2*id-i],mx-i):1;
		//额外匹配的部分 
		while(t[i-p[i]]==t[i+p[i]]) ++p[i];
		if(mx<i+p[i]){ 
   
			mx=p[i]+i;
			id=i;
		}
		//保留最长的部分 
		if(resl<p[i]){ 
   
			resl=p[i];
			resc=i;
		}
	}
	/*最长的回文串的范围为 (最长的回文串的中间位置-最长的回文串的半径)/2的位置 到 (最长的回文串的半径-1)的位置 */ 
	cout << s.substr((resc-resl)/2,resl-1);
	return 0;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/140935.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • c语言pdb文件,VISUAL c+中的pdb文件及其作用「建议收藏」

    c语言pdb文件,VISUAL c+中的pdb文件及其作用「建议收藏」VISUALc+中的pdb文件及其作用程序数据库(PDB)文件保存着调试和项目状态信息,使用这些信息可以对程序的调试配置进行增量链接。当以/ZI或/Zi(用于C/C++)生成时,将创建一个PDB文件。在VisualC++中,/Fd选项用于命名由编译器创建的PDB文件。当使用向导在VisualStudio中创建项目时,/Fd选项被设置为创建一个名为projec…

    2022年6月2日
    32
  • JSF标签_img标签详解

    JSF标签_img标签详解1.JSF入门藉由以下的几个主题,可以大致了解JSF的轮廓与特性,我们来看看网页设计人员与应用程序设计人员各负责什么。1.1简介JSFWeb应用程序的开发与传统的单机程序开发在本质上存在着太多的差异,

    2022年8月5日
    8
  • group by 与 where, having以及顺序

    group by 与 where, having以及顺序1.GROUPBY子句必须出现在WHERE子句之后,ORDERBY子句之前.HAVING语句必须在ORDERBY子句之后。(where先执行,再groupby分组;groupby先分组,having在执行。)2.除聚集计算语句外,SELECT语句中的每个列都必须在GROUPBY子句中给出。count()为聚集函数,vend_id在后面groupby中有,所以select后面有。sel…

    2022年5月25日
    64
  • 移动端开发技术浅析

    移动端开发技术浅析移动端开发技术浅析目录APK下载概述技术介绍技术对比参考资料1.APK下载百度云链接:https://pan.baidu.com/s/1pLp44Fh2.概述“一次编码,处处运行”永远是程序员们的理想乡。二十年前Java正是举着这面大旗登场,击败了众多竞争对手。但是时至今日,事实已经证明了Java笨重的体型和缓慢的发展显然已经很难再抓住这个时代快速跃动的脚步。在

    2022年6月24日
    25
  • mac Navicat Premium15 激活码【永久激活】

    (mac Navicat Premium15 激活码)本文适用于JetBrains家族所有ide,包括IntelliJidea,phpstorm,webstorm,pycharm,datagrip等。IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.html…

    2022年3月21日
    179
  • Java 生成二维码_二维码生成规则

    Java 生成二维码_二维码生成规则利用Java生成二维码生成二维码的依赖下载:点击下载代码:packagecom.shuai.test02;importcom.swetake.util.Qrcode;importjavax.imageio.ImageIO;importjava.awt.*;importjava.awt.image.BufferedImage;importjava.io.File;importjava.nio.charset.StandardCharsets;publicclas

    2025年6月23日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号