对称子字符串的最大长度

本文转自:http://zhedahht.blog.163.com/blog/static/25411174201063105120425/

http://blog.youkuaiyun.com/v_july_v/article/details/6712171

题目:输入一个字符串,输出该字符串中对称的子字符串的最大长度。比如输入字符串“google”,由于该字符串里最长的对称子字符串是“goog”,因此输出4。

分析:可能很多人都写过判断一个字符串是不是对称的函数,这个题目可以看成是该函数的加强版。

引子:判断字符串是否对称

要判断一个字符串是不是对称的,不是一件很难的事情。我们可以先得到字符串首尾两个字符,判断是不是相等。如果不相等,那该字符串肯定不是对称的。否则我们接着判断里面的两个字符是不是相等,以此类推。基于这个思路,我们不难写出如下代码:

//判断str是否为对称字串
bool isSymmetry(char* str,int len)
{
	if(NULL == str || len <= 0)
	{
		invaluedInput = true;
		return false;
	}
	
	invaluedInput = false;
	char* pBegin = str;
	char* pEnd = str+len-1;
	while(pBegin < pEnd)
	{
		if(*pBegin != *pEnd)
			return false;
		pBegin++;
		pEnd--;
	}
	return true;
}

解法:On3)的算法

现在我们试着来得到对称子字符串的最大长度。最直观的做法就是得到输入字符串的所有子字符串,并逐个判断是不是对称的。如果一个子字符串是对称的,我们就得到它的长度。这样经过比较,就能得到最长的对称子字符串的长度了。于是,我们可以写出如下代码:

int GetLongestSymLength(char* str,int len)
{
	if(NULL == str || len <= 0)
	{
		invaluedInput = true;
		return 0;
	}

	invaluedInput = false;

	char* pBegin = str;
	char* pEnd;
	int maxLength = 1;
	int subStrLen = 0;
	for(pBegin; pBegin<str+len-1; pBegin++)
	{
		for(pEnd=pBegin+1; pEnd<str+len; pEnd++)
		{
			subStrLen = pEnd - pBegin + 1;
			if(isSymmetry(pBegin,subStrLen))
			{
				//如果要求最长对称字串的内容,则为pBegin到pEnd之间的内容,可以记录下来返回
				if(subStrLen > maxLength)
					maxLength = subStrLen;
			}
		}
	}

	return maxLength;
}
我们来分析一下上述方法的时间效率。由于我们需要两重 while 循环,每重循环需要 O n )的时间。另外,我们在循环中调用了 IsSymmetry ,每次调用也需要 O n )的时间。因此整个函数的时间效率是 O n3 )。

本程序的全部代码如下:

#include <iostream>
#include <string>
using namespace std;

bool invaluedInput = false;

//判断str是否为对称字串
bool isSymmetry(char* str,int len)
{
	if(NULL == str || len <= 0)
	{
		invaluedInput = true;
		return false;
	}
	
	invaluedInput = false;
	char* pBegin = str;
	char* pEnd = str+len-1;
	while(pBegin < pEnd)
	{
		if(*pBegin != *pEnd)
			return false;
		pBegin++;
		pEnd--;
	}
	return true;
}

int GetLongestSymLength(char* str,int len)
{
	if(NULL == str || len <= 0)
	{
		invaluedInput = true;
		return 0;
	}

	invaluedInput = false;

	char* pBegin = str;
	char* pEnd;
	int maxLength = 1;
	int subStrLen = 0;
	for(pBegin; pBegin<str+len-1; pBegin++)
	{
		for(pEnd=pBegin+1; pEnd<str+len; pEnd++)
		{
			subStrLen = pEnd - pBegin + 1;
			if(isSymmetry(pBegin,subStrLen))
			{
				//如果要求最长对称字串的内容,则为pBegin到pEnd之间的内容,可以记录下来返回
				if(subStrLen > maxLength)
					maxLength = subStrLen;
			}
		}
	}

	return maxLength;
}

int main()
{
	//测试用例
	//char str[] = "aaaaaabdaaaaa";
	//char str[] = "aaaaaaaaaaa";
	//char str[] = "abedef";
	//char str[] = "google";
	char str[] = "goodle";
	int len = strlen(str);
	cout<<len<<endl;
	int maxLen = GetLongestSymLength(str,len);
	cout<<maxLen<<endl;
	return 0;
}

解法二:On2)的算法:

通常On3)不会是一个高效的算法。如果我们仔细分析上述方法的比较过程,我们就能发现其中有很多重复的比较。假设我们需要判断一个子字符串具有aAa的形式(AaAa的子字符串,可能含有多个字符)。我们先把pFirst指向最前面的字符a,把pLast指向最后面的字符a,由于两个字符相同,我们在IsSymmetry函数内部向后移动pFirst,向前移动pLast,以判断A是不是对称的。接下来若干步骤之后,由于A也是输入字符串的一个子字符串,我们需要再一次判断它是不是对称的。也就是说,我们重复多次地在判断A是不是对称的。

造成上述重复比较的根源在于IsSymmetry的比较是从外向里进行的。在判断aAa是不是对称的时候,我们不知道A是不是对称的,因此需要花费On)的时间来判断。下次我们判断A是不是对称的时候,我们仍然需要On)的时间。

如果我们换一种思路,我们从里向外来判断。也就是我们先判断子字符串A是不是对称的。如果A不是对称的,那么向该子字符串两端各延长一个字符得到的字符串肯定不是对称的。如果A对称,那么我们只需要判断A两端延长的一个字符是不是相等的,如果相等,则延长后的字符串是对称的。因此在知道A是否对称之后,只需要O1)的时间就能知道aAa是不是对称的。

代码如下:

int GetLongestSymLength2(const char* str,int len)
{
	if(NULL == str || len<1)
	{
		invaluedInput = true;
		return 0;
	}
	invaluedInput = false;

	int i,j,maxLength = 0;
	//i的位置为对称字符串的中间位置
	for(i=0;i<len; i++)
	{
		//对称字符串长度为奇数
		for(j=0; (i-j>=0)&&(i+j)<len; j++)
			if(str[i-j] != str[i+j])
				break;
		if(j*2-1 > maxLength)
			maxLength = j*2-1;
		//对称字符串长度为偶数
		for(j=0; (i-j>=0)&&(i+j+1<len); j++)
			if(str[i-j] != str[i+j+1])
				break;
		if(j*2 > maxLength)
			maxLength = j*2;
	}
	return maxLength;
}
 代码稍微难懂一点的地方就是内层的两个 for 循环,它们分别对于以 i 为中心的,分长度为奇数和偶数的两种情况,整个代码遍历中心位置 i 并以之扩展,找出最长的对称字符串。

由于子字符串的长度可能是奇数也可能是偶数。长度是奇数的字符串是从只有一个字符的中心向两端延长出来,而长度为偶数的字符串是从一个有两个字符的中心向两端延长出来。因此我们的代码要把这种情况都考虑进去。

在上述代码中,我们从字符串的每个字符串两端开始延长,如果当前的子字符串是对称的,再判断延长之后的字符串是不是对称的。由于总共有On)个字符,每个字符可能延长On)次,每次延长时只需要O1)就能判断出是不是对称的,因此整个函数的时间效率是On2)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值