LeetCode(1),最长回文子串

256 阅读3分钟

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

  • 示例1

输入: "babad"

输出: "bab"

注意: "aba" 也是一个有效答案。

  • 示例 2

输入: "cbbd"

输出: "bb"

解:

最简单的思路是目标字符串进行逐一验证,由此可得以下解法,

char * longestPalindrome(char * s){
    int length = strlen(s);//传入的字符串长度
	int sub_len = 0;//保存当前最长回文串的长度
	int pos1 = 0;//初始头部
	int pos2;//初始尾部
	int i;
	char *result = (char *)malloc(length+1);//分配一段内存保存结果;	
	char *start ;   //start指向字符串初始位置
	char *end ;     //end指向字符串末尾
    
    if (length==0) {
		*result = '\0';
		return result;
	}
	while ( (pos1<length)  || sub_len<(length-pos1) )//当start指针未移动到末尾或者未查找子串长度大于已找到最长回文串的长度 则继续查找
	{
		pos2 = length - 1;
		while ((pos2-pos1+1)>sub_len) {
			start = s + pos1;
			end = s + pos2;
			//判断从pos1到pos2的子串是否是回文串
			while ( *start==*end && end>start) {
				start++;
				end--;
			}
			
			if (end <= start) {//是回文串
				if (sub_len< pos2 - pos1 + 1) {
					sub_len = pos2 - pos1 + 1;
					for (i = pos1; i <= pos2; i++)//复制
					{
						*(result + i - pos1) = *(s + i);
					}
					*(result + i - pos1) = '\0';
				}
				
				break;

			}
			else {
				pos2--;
			}
		}
		pos1++;
	}

	return result;

}

遗憾的是,这样做的时间复杂度很差,以上算法用pos1从目标串的第一个字符起往后遍历子串起点,用pos2从目标串末尾往前遍历子串终点,然后判断起点为pos1,终点为pos2的子串是否是回文串,若是则复制到result中,并记录该回文串长度。假设目标串的长度为n,则截取子串的时间复杂度为O(n^2),检验是否回文串的时间复杂度为O(n)。如何进一步降低时间复杂度?力扣官网给出了提示:利用之前找出的回文串来判断之后的子串,从而简化判断过程。假设已经找到一个三字符的回文串“aba”,我们由“aba”这个回文串,可以判断“xabay”是否是回文串:只需要比较x位置字符和y位置字符是否相等。同理可推广到更多位数的字符串。由此得出改进的解法:

char * longestPalindrome(char * s) {

	char *result;
	int length = strlen(s); //传入的字符串长度
	int n;   //子串长度,从长度为2的子串开始查找
	int i,j;   //初始位置
	int find=1;//当前回文串长度
	int mark[1001][1001];      /*标记矩阵,
                                  元素mark[i][j]
                                    用来标记从第mark[i][j]个字符起长度为i的子串是否为回文串。若存储空间不足,可以改用链表。
	                        	*/
	if(strlen(s)==0){ //若字符串为空
		result = (char *)malloc(sizeof(char));
		*result = '\0';
		return result;
	}

	//初始化
	for ( i = 1; i <= length; i++)
	{
		mark[1][i] = 1;
	}
	//判断偶数长度的子串与判断奇数子串的参照不一样,所以还要初始化mark[2][j];
	for ( i = 0; i+1 < length; i++)
	{ 
		if (*(s+i)==*(s+i+1)) {
			mark[2][i + 1] = 1;
			if (2 != find)
				find = 2;
		}
	}

	
	//n>=3
	n = 3; 
	while (n<=length && (n-find)<=2) {

		for ( i = 2; i < length; i++)//去掉边界上的值
		{
			if (mark[n-2][i]==1) {
				
				if (*(s+i-2)==*(s+n+i-3)) {//判断
					mark[n][i - 1] = 1; 
					if (n != find)
						find = n;
				}
			}
		}

		n++;
		
	}
	
	
	//返回结果
	for (j = 1; mark[find][j]!=1; j++);

	result = (char *)malloc((find+1)*sizeof(char));
	length = j+find;

	for ( i = j; i < length; i++)
	{
		*(result + i - j) = *(s+i-1);
	}
	
	*(result+i-j)='\0';//添加末尾标志
	
	return result;
	
}