O(n^3)、O(n^2)和O(n)求最长回文子串

其实求最长回文子串就是求最长对称子串

【法一】

最容易想到的就是找出所有的子串(O(n^2)),判断每个字符是否对称(O(n)),所以总的时间复杂度是O(n^3)。

【法二】法一是“从外向里”判断,相反,可以“从里向外”判断,时间复杂度O(n^2),代码如下

void GetLongestSymmetircalSubstring(char *pStr)
{
	int symmetricalLen=1;
	char *pChar=pStr;
	char maxSubsring[100];
	memset(maxSubsring,0,100);
	maxSubsring[0]=*pStr;

	while(*pChar!='\0')
	{
		char *pFirst=pChar-1;
		char *pLast=pChar+1;
		//处理奇数长度的子串
		while(pFirst>=pStr && *pLast!='\0' && *pFirst==*pLast)
		{
			pFirst--;
			pLast++;
		}

		int newLen=pLast-pFirst-1;
		if (newLen>symmetricalLen)
		{
			symmetricalLen=newLen;
			strCpy(maxSubsring,pFirst+1,pLast-1);
		}
                  
                 //处理偶数长度的子串
		pFirst=pChar;
		pLast=pChar+1;
		while (pFirst>=pStr && *pLast!='\0' && *pFirst==*pLast)
		{
			pFirst--;
			pLast++;
		}

		newLen=pLast-pFirst-1;
		if (newLen>symmetricalLen)
		{
			symmetricalLen=newLen;
			strCpy(maxSubsring,pFirst+1,pLast-1);
		}
		pChar++;
	}
	cout<<"字符串pStr的最长对称子串长度是:"<<symmetricalLen<<"\t"<<"子串为:"<<endl;
	
	cout<<maxSubsring<<endl;
}

void main()
{
	char str[100]="google";
	GetLongestSymmetircalSubstring(str);
	
}


注:法一及法二参见何海涛老师的博客http://zhedahht.blog.163.com/blog/static/25411174201063105120425/

 

【法三】O(n)求最长回文子串---Manacher算法

本法的优点不仅是O(n)复杂度,而且不用考虑奇数子串和偶数子串的情况。

Manacher算法详细解析请看文末的参考文章,以下代码是我自己试着写的。

int Min(int& a,int& b)
{
	if(a<=1||b<=1)
		return 1;
	if(a<b)
		return a;
	return b;
}


void GetLongestPalindromicSubstring(char *s)
{
	char *pCh=s;
	int len=0;  //原字符串pStr的长度
	while (*pCh++!='\0')
		len++;

	int newLen=2*len+2;//扩展后字符串长度
	char *str=new char[newLen+1];//分配多分配一个内存,用于存储'\0'
	str[0]='$';
	str[1]='#';
	str[newLen]='\0';//以'\0'结尾
	
	int i;
	for (i=0;i<len;i++)//构建新字符串
	{
		str[2*i+2]=s[i];
		str[2*i+3]='#';
	}

	cout<<"原来字符串:";//输出原字符串,做参考用,可有可无
	for (i=0;i<len;i++)
	{
		cout<<s[i]<<" ";//字符间加空格,方便观看
	}
	cout<<endl;

	i=0;
	cout<<"扩展字符串:";
	while(str[i]!='\0')//这个循环可有可无,输出扩展的字符串仅做参考用,字符间加了个空格
		cout<<str[i++]<<" ";
	cout<<endl;

	int *p=new int[newLen];//构造数组p[],p[i]为扩张数组中i位置的最长回文串半径
	memset(p,1,newLen*sizeof(int));//初始化为1,即每一个p[i]的回文串初始为1
	p[0]=0;//扩展串str第一个字符为'$'

	int id=0;
	int max=0;
	
	for(i=1;i<newLen;i++)//构造数组p的过程,也是本算法的核心所在,详见文末参考文章
	{
		if (max>i)
		{
			int diff=max-i;//用变量diff代替max-i纯是为了调用Min函数,因为max-i为常量值,不能调用Min
			p[i]=Min(p[2*id-i],diff);
		}
		else
			p[i]=1;
		while(str[i+p[i]]==str[i-p[i]])
			p[i]++;
		if (p[i]+i>max)
		{
			max=p[i]+i;
			id=i;
		}
	}

	cout<<"下标 数 组:";
	for (i=0;i<newLen;i++)//输出数组p,仅做参考用
	{
		cout<<p[i]<<" ";
	}
	cout<<endl;
	
	int rad=0;
	int j,index=0;
	for (i=1;i<newLen;i++)
	{
		if(p[i]-1>rad)
		{	
			rad=p[i]-1;//p[i]中的最大值-1即为原字符串中的最大回文串长度rad
			index=i;//index记录具有最大回文串长度的扩展串中的字符的下标
		}
	}
	cout<<"最大回文子串长度为:"<<rad<<endl;
	//以下相当于由扩展字符串下标映射回原字符串的下标
	if (str[index]=='#')//如果具有最长回文串的字符是'#',作如下处理,画图更容易理解
	{
		i=(index-3)/2;
		for (j=i-(rad/2-1);j<=i+rad/2;j++)
		{
			cout<<s[j];
		}
		cout<<endl;
	}
	else //如果具有最长回文串的字符是原字符串中的字符,作如下处理
	{
		i=(index-2)/2;
		for (j=i-rad/2;j<=i+rad/2;j++)
		{
			cout<<s[j];
		}
		cout<<endl;
	}
}


void main()
{
	char s[]="waabwswfd";
	GetLongestPalindromicSubstring(s);
}


测试1:字符串"waabwswfd"

测试2:字符串"google"

测试3:字符串"cdfacbghgbcadba"

 

Manacher算法比较好的参考文章:

                1、 O(n) 求 最长回文子串   http://www.cnblogs.com/wuyiqi/archive/2012/06/25/2561063.html

                2、Manacher算法    http://blog.csdn.net/adrastos/article/details/9093779#

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值