(算法)最长回文子串

题目:

求一个字符串的最长回文子串

思路:

1、暴力枚举

最容易想到的就是暴力破解,列举每一个子串,然后根据回文的定义判断是不是回文,找到最长的那个。

求每一个子串的时间复杂度为O(N^2),判断子串是不是回文的时间复杂度为O(N),所以时间复杂度为O(N^3)。

2、动态规划

回文字符串的子串也是回文,比如P[i,j](表示以i开始以j结束的子串)是回文字符串,那么P[i+1,j-1]也是回文字符串。这样最长回文子串就能分解成一系列子问题了。

这样需要额外的空间是O(N^2),时间复杂度也是O(N^2)。

状态转移方程:

当str[i]=str[j] dp[i][j]=dp[i+1][j-1]

当str[i]!=str[j] dp[i][j]=0

初始状态:

dp[i][i]=1

dp[i][i+1]=1 if str[i]=str[i+1]

3、中心扩展

中心扩展就是把给定的字符串的每一个字符当做中心,向两边扩展,这样来找最长的子回文串。时间复杂度为O(N^2)。

但是要考虑两种情况:

如aba,这样长度为奇数。

如abba,这样长度为偶数。

4、Manacher法

待续

代码:

#include<iostream>
using namespace std;

// brute force
string findLongestPalindrome_1(string &str){
	int length=str.size();
	int maxLength=0;
	int start;

	for(int i=0;i<length;i++){
		for(int j=i+1;j<length;j++){
			int left,right;
			for(left=i,right=j;left<right;left++,right--){
				if(str[left]!=str[right])
					break;
			}
			if(left>=right && (j-i)>maxLength){
				maxLength=j-i+1;
				start=i;
			}
		}
	}

	if(maxLength>0)
		return str.substr(start,maxLength);
	return NULL;
}

// dynamic programming
string findLongestPalindrome_2(string &str){
	const int length=str.size();
	int maxLength=1;
	int start;

	bool dp[length][length];

	for(int i=0;i<length;i++)
		for(int j=0;j<length;j++)
			dp[i][j]=false;

	for(int i=0;i<length;i++){
		dp[i][i]=true;
		if(i<length-1 && str[i]==str[i+1]){
			dp[i][i+1]=true;
			start=i;
			maxLength=2;
		}
	}

	for(int len=3;len<=length;len++){
		for(int i=0;i<=length-len;i++){
			int j=i+len-1;
			if(dp[i+1][j-1] && str[i]==str[j]){
				dp[i][j]=true;
				start=i;
				maxLength=len;
			}
		}
	}

	if(maxLength>=2)
		return str.substr(start,maxLength);
	return NULL;
}

// pivot expand
string findLongestPalindrome_3(string &str){
	const int length=str.size();
	int maxLength=1;
	int start;

	for(int i=0;i<length;i++){
		int left=i-1;
		int right=i+1;
		while(left>=0 && right<=length-1 && str[left]==str[right]){
			if(right-left+1>maxLength){
				start=left;
				maxLength=right-left+1;
			}
			left--;
			right++;
		}
	}

	for(int i=0;i<length;i++){
		int left=i;
		int right=i+1;
		while(left>=0 && right<=length-1 && str[left]==str[right]){
			if(right-left+1>maxLength){
				start=left;
				maxLength=right-left+1;
			}
			left--;
			right++;
		}
	}

	if(maxLength>0)
		return str.substr(start,maxLength);
	return NULL;
}

int main(){
	string str="ababcddcbbd";
	cout<<findLongestPalindrome_1(str)<<endl;
	cout<<findLongestPalindrome_2(str)<<endl;
	cout<<findLongestPalindrome_3(str)<<endl;
	return 0;
}

  

时间: 2024-11-06 07:30:01

(算法)最长回文子串的相关文章

Manacher算法----最长回文子串

题目描述 给定一个字符串,求它的最长回文子串的长度. 分析与解法 最容易想到的办法是枚举所有的子串,分别判断其是否为回文.这个思路初看起来是正确的,但却做了很多无用功,如果一个长的子串包含另一个短一些的子串,那么对子串的回文判断其实是不需要的.同时,奇数和偶数长度还要分别考虑. Manacher算法可以解决上述问题,并在O(n)时间复杂度内求出结果.下面我们来看一下Manacher算法. 首先,为了处理奇偶的问题,在每个字符的两边都插入一个特殊的符号,这样所有的奇数或偶数长度都转换为奇数长度.比

Manacher算法——最长回文子串(O(n))

1 public static int Manacher(String A,int n){ 2 char AA[]=A.toCharArray(); 3 char BB[]=new char[2*n+3]; 4 int k=0; 5 for(int i=1;i<=2*n+1;i=i+2){ 6 BB[i]='#'; 7 if(i+1<=2*n+1)BB[i+1]=AA[k++]; 8 } 9 BB[0]='$';//防止数组越界 10 BB[2*n+2]='&';//防止数组越界 11

[hiho 01]最长回文子串、Manacher算法

题目描述 - 基础方法:枚举子串,判断是否为回文串. - 改进:枚举中间位置,向两侧拓展. - 再改进:利用以前的信息,使得不用每个新位置都从长度1开始拓展. - 优化:将字符串预处理为奇数长度以避免考虑条件分支. - 再优化:开头加入特殊字符避免考虑边界. Manacher 算法: id 是中心点,mx 是其边界.P[i] 表示以 i 为中心的最长回文子串的折半长度. 只要 i < mx, 以 i 为中心的回文子串就可以不必从长度1开始找,而从min{P[j], mx - i}开始(其中j为i

manacher求最长回文子串算法

原文:http://www.felix021.com/blog/read.php?2040 首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号.比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#. 为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如 @#a#b#a#(注意,下面的代码是用C语言写 就,由于C语言规范还要求字符串末尾有一个'\0'所以正好OK,但其

最长回文子串 - Manacher算法

算法思想: 设有字符串s[] = "121" 第一步:通过在每个字符左右都添加一个特殊字符,把奇数长度和偶数长度的字符串都转化成奇数(例如. "121" 加上特殊字符后变成"#1#2#1" ),同时也可在开头再加一个特殊字符,以便于忽略越界问题(如上例"121"变成"$#1#2#1#"  此时开头的特殊字符$和字符串末尾的\0与此串中其他字符都不同,即可忽略越界问题),此时字符串变成 s[] = "

O(n) 求最长回文子串的 Manacher 算法

Manacher是一个可以在O(n)的时间内求出一个长度为n的字符串的算法. 以为回文子串有偶数长度,也有奇数长度,分别处理会很不方便. 所以在每两个字符中间插入一个无关字符,如‘#’,这样所有的回文子串都变为奇数长度. 两端在添加不同的无关字符防止匹配时越界. 如: abba 变成 $#a#b#b#a#& 预处理代码: void Prepare() { l = strlen(Str); S[0] = '$'; for (int i = 0; i <= l - 1; i++) { S[(i

计算字符串的最长回文子串 :Manacher算法介绍

在介绍算法之前,首先介绍一下什么是回文串,所谓回文串,简单来说就是正着读和反着读都是一样的字符串,比如abba,noon等等,一个字符串的最长回文子串即为这个字符串的子串中,是回文串的最长的那个. 计 算字符串的最长回文字串最简单的算法就是枚举该字符串的每一个子串,并且判断这个子串是否为回文串,这个算法的时间复杂度为O(n^3)的,显然无法令人 满意,稍微优化的一个算法是枚举回文串的中点,这里要分为两种情况,一种是回文串长度是奇数的情况,另一种是回文串长度是偶数的情况,枚举中点再判断是否 是回文

[hdu3068 最长回文]Manacher算法,O(N)求最长回文子串

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3068 题意:求一个字符串的最长回文子串 思路: 枚举子串的两个端点,根据回文串的定义来判断其是否是回文串并更新答案,复杂度O(N3). 枚举回文串的对称轴i,以及回文半径r,由i和r可确定一个子串,然后暴力判断即可.复杂度O(N2). 在上一步的基础上,改进判断子串是否是回文串的算法.记fi(r)=(bool)以i为对称轴半径为r的子串是回文串,fi(r)的值域为{0, 1},显然fi(r)是关于r

Manacher&#39;s algorithm: 最长回文子串算法

Manacher 算法是时间.空间复杂度都为 O(n) 的解决 Longest palindromic substring(最长回文子串)的算法.回文串是中心对称的串,比如 'abcba'.'abccba'.那么最长回文子串顾名思义,就是求一个序列中的子串中,最长的回文串.本文最后用 Python 实现算法,为了方便理解,文中出现的数学式也采用 py 的记法. 在 leetcode 上用时间复杂度 O(n**2).空间复杂度 O(1) 的算法做完这道题之后,搜了一下发现有 O(n) 的算法.可惜

【最长回文子串】HDU3068最长回文【Manacher算法】

一张图领悟Manacher算法,计算字符串最长回文子串 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3068 Problem Description 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 回文就是正反读都是一样的字符串,如aba, abba等 Input 输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S 两组case之间由空行隔开(该空行不用处理)