最长回文子串


解法一:暴力解法,取原字符串的每一个子串,判断子串是否为回文。
           O(n^3)。 很多短子字符串在长子字符串中比较过,这导致了大量的冗余判断,根本原因是:对字符串对称的判断是由外向里进行的。
string longestPalindrome(string s)
{
	if (s.size()!= NULL)
	{
		string maxString;
		for (int k = 0; k < s.size();k++)
		{
			int i = k;
			int j = s.size()-1;   //求最长回文,从最后面开始查找,找到的第一个回文串必是下标k开头的最长回文
			while (j >= i)  // 
			{
				int t = j;
				while(i<=t && s[i]==s[t])
				{
					i++;
					t--;
				}
				if (i>=t)  //说明从k开始,j结尾的子串为回文串
				{
					string temp = s.substr(k,j-k+1);
					if (temp.size()>maxString.size())
					{
						 maxString = temp;
					}
					break;  // 此时的串必定是以下标k开头的最长的回文串,退出本次while循环,从下一个k开始
				}
				else
				{
					i = k;
					j--;
				}
					
			}
				
		}
		return maxString;
	}
	return s;	
}


解法二:将原串s置逆得到s1,根据回文的性质,则逆置后的串s1中也含有该回文串;要求最长回文串,只需求s,s1的最长公共子串即可。原问题转化为求最长公共子串问题;

解法三: 对给定的字符串S,分别以该字符串S中的每一个字符C(回文总长度为奇数)和 每两个字符C1C2(回文长度为偶数)为中心,向两边扩展,记录下以字符C和C1C2为中心的回文子串的长度。
思想:相对于暴力解法, 换一种思路,从里向外来判断。也就是先判断子字符串(如dd或dcd)是不是对称的。
          如果它(dd)不是对称的,那么向该子字符串两端各延长一个字符得到的字符串肯定不是对称的。
          如果它(dd)对称,那么只需要判断它(dd)两端延长的一个字符是不是相等的,如果相等,则延长后的字符串是对称的。
//由于子字符串的长度可能是奇数也可能是偶数:
//长度是奇数的字符串是从只有一个字符的中心向两端延长出来,而长度为偶数的字符串是从一个有两个字符的中心向两端延长出来。因此程序中要把这两种情况都考虑进去。 
/*
string longestPalindrome_2(string s) 
{
	int length = s.size();
	if (length)
	{
		int maxLength = 0;
		int begin = 0,end = 0;  //最长子串起始、结束坐标
		//回文子串的长度为奇数
		for(int i = 0; i < s.size();i++)  
		{
			int left = i-1;   
			int right = i+1;  //子串的长度为奇数
			int maxOddLength = 1; //奇数长度回文子串最短为1
			while(left>=0 && right<length && s[left]==s[right])  
			{
				left--;
				right++;  //向两边扩张
				maxOddLength+=2;
			}
			if (maxOddLength>maxLength)
			{
				maxLength = maxOddLength;
				begin = left+1;
				end = right-1;
			}
		}

		//回文子串的长度为偶数
		for (int i = 0; i < s.size();i++)
		{
			int left = i;   //子串的长度为偶数
			int right = i+1; 
			int maxEvenLength = 0; //偶数长度子串最短为0
			while(left>=0 && right<length && s[left]==s[right])  
			{
				left--;
				right++;  //向两边扩张
				maxEvenLength+=2;
			}
			if (maxEvenLength>maxLength)
			{
				maxLength = maxEvenLength;
				begin = left+1;
				end = right-1;
			}
		}
		return s.substr(begin,end-begin+1);

	}
	else
	{
		return s;
	}


}

解法四:Manacher算法。
#include <iostream>
#include <algorithm>
#include <string>
#include <stdlib.h>
#include <vector>
using namespace std;

//由于在求回文过程中分奇数回文和偶数回文,为了统一处理,通过对字符串添加特殊字符(串中不会出现的字符如#),使得处理后的字符串总数均为奇数
//假如原始字符串abba; 处理后的串为#a#b#b#a# , 总长为 2*4+1 ; 若原始字符串为abcba, 处理后的串为#a#b#c#b#a# ,总长为 2*5+1 ;
//可以看到第一个字符的回文半径为1,第二个字符的回文半径为2;
//
string Manacher(string s) 
{
	int length = s.size();
	int *rad = new int[2*length+1];  // 添加特殊字符后保存各字符的回文半径。
	char *ch = new char[2*length+2];
	for (int i = 0;i < s.size();i++)
	{
		ch[2*i]='#';
		ch[2*i+1]=s[i];
	}
	ch[2*length]='#';
	ch[2*length+1]='\0';
	string str(ch);
	length = str.size();
	delete[] ch;
	rad[0] = 1;
	int ManacherLocation = 0; //最大回文串开始下标;
	int maxRad = 1;  //最大回文串半径
	int index = 0; //之前的某个回文串的下标;
	int rightbounder = 0; // 回文右边界
	
	for (int i = 1;i < length;i++)
	{
		if (i<=rightbounder)
		{
			int j = 2*index-i;  // i关于index的对称点
			if (rightbounder-i>rad[j])
			{
				rad[i] = rad[j];
			}
			else
			{
				//rad[i] = std::min(rad[j],rightbounder-i+1);  // 取两者最小值,由于上面if,可以直接写成如下
				rad[i] = rightbounder-i+1;  // !!!
				int begin = rightbounder-2*(rightbounder-i)-1;
				int end = rightbounder+1;
				while(begin>=0 && end<length && str[begin]==str[end])
				{
					rad[i]++;
					begin--;
					end++;
				}
				
				if (i+rad[i]-1 > rightbounder)  // 判断要加!!!,只有大于原来右边界才需要改变,否则说明还在原来index的回文区间内,不需要改变index和rightbounder
				{
					index = i;   //这两句不能写在下面的if里面,因为右边界的更新只要满足:i点对称点j,j的回文半径rad[j]大于或等于(rightbounder-i)且i点右边界大于原来index的右边界时 ,此时右边界更新,index更新
					rightbounder = i+rad[i]-1;  
				}
				
				
				if (rad[i] > maxRad)  //仅用于保存最大回文半径及中心点
				{
					maxRad = rad[i];
					ManacherLocation = i;
				}
			}
		}
		else
		{
			rad[i] = 1;
			int begin = i-1;
			int end = i+1;
			while(begin>=0 && end<length && str[begin]==str[end])
			{
				rad[i]++;
				begin--;
				end++;
			}

			//index = i;
			//rightbounder = i+rad[i]-1;
			
			if (i+rad[i]-1 > rightbounder)  // 判断要加!!!,只有大于原来右边界才需要改变,否则说明还在原来index的回文区间内,不需要改变index和rightbounder
			{
				index = i;   //这两句不能写在下面的if里面,因为右边界的更新只要满足:i点对称点j,j的回文半径rad[j]大于或等于(rightbounder-i)且i点右边界大于原来index的右边界时 ,此时右边界更新,index更新
				rightbounder = i+rad[i]-1;  
			}

			if (rad[i] > maxRad)
			{
				maxRad = rad[i];
				ManacherLocation = i;
			}
		}	
	}
	
	string resting = str.substr(ManacherLocation-maxRad+1,2*maxRad-1);
	
	char *p = new char[2*maxRad-1];
	int count = 0;  //大意,将count声明为char型了;!!!
	for (int i = 0; i < 2*maxRad-1;i++)
	{
		if (resting[i]!='#')
		{
			p[count] = resting[i];
			count++;
		}
	}
	string res(p,count); //创建一个string对象,被初始化为p所指向数组的前n个元素; !!!!
	delete[] p;
	return res;

	//使用string 的 迭代器
	/*
	string::iterator iter = resting.begin();
	string ss;
	while (iter != resting.end())
	{
		if (*iter != '#')
		{
			ss.push_back(*iter);  //删除迭代器iter所指向的元素,返回一个迭代器,指向被删除元素后面的元素;	
		}
		iter++;
	}
	return ss;
	*/
	
}

int main()
{
	string s = "aaaabbbbbbbbbbccccccccccddddddddddccccccccccbbbbbbbbbbaaaa"; 
	string res  = Manacher(s);
	cout<<res<<endl;
	system("pause");
	return 0;
}

http://www.cnblogs.com/heyonggang/p/3386724.html




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值