寻找最长回文字符串

首先讲解一种简单容易理解的暴力解法:复杂度为O(n^2)

解题思路是:第一,定义一个pStr指向字符串str,再定义一个p指向pStr,q指向pStr+1;

第二,找出一个字符*p与其下一个字符*q相同位置,比如oo,num++,index = p;然后比较这两个相同字符*p,*q两边的字符是否相等,如果相等再向两边扩展p--,q++(p>str&&q!=‘\0‘)。如果p指向首部,即p=str,则调出while循环,再比较一次if(*p == *q),num++,index = q.

第三,如果发现连续两个字符不相等,则让pStr++,p=pStr,q =pStr+1.

第四,通过maxNum和maxIndex记录下最长回文的数目和位置。

#include <iostream>
using namespace std;

void GetLongestSymmetricalLength(char* str)
{
    if(str==NULL)
        return;

    char* pStr = str;
    char* p = pStr;
    char* q = pStr+1;
    int num=0;
    int maxNum=0;;
    char* index = pStr;
    char* maxIndex = index;

    while(*pStr != ‘\0‘)
    {
        while((*p != *q))
        {
            num = 0;
            pStr++;
            p = pStr;
            q = pStr+1;
        }
        while((*p == *q)&&(p > str)&&(*q != ‘\0‘))
        {
            num++;
            index = p;
            p--;
            q++;
        }
        if((p == str)&&(*p == *q)&&(*q != ‘\0‘))
        {
            num++;
            index = p;
        }

        if(num > maxNum)
        {
            maxNum = num;
            maxIndex = index;
        }

        pStr++;
        p = pStr;
        q = pStr+1;
    }
    cout << "Result: " ;
    for(int i=0;i<2*maxNum;++i)
        cout << *maxIndex++ << " ";
    cout << endl;
    cout << "maxNum: "  << maxNum << endl;
}

int main()
{
    char* str = "abbacaacab";
    GetLongestSymmetricalLength(str);

    return 0;
}

  

以下空间复杂度和时间复杂度均为O(n)

思路:

复杂度为O(n),即指遍历一遍数组,因而必须创建一数组保存已走过字符的长度信息(之前每个字符的最长回文串)。

同时,如”aba“最长回文长度为3,中心为’b‘;”aa“最长回文长度为2,中心却是两字符中心,如此在统计长度时,需对奇偶串长分别比较,有什么办法使其统一吗?——插入特殊字符,如‘#’,”aba“为”#a#b#a#“,”aa“为”#a#a#“,如此,奇偶情况的最长长度都分别有了对应的中心。具体如下:

1 、在各个字符间插入特殊字符‘#‘,将字符S转换为T,如S = “abaaba", T = "#a#b#a#a#b#a#”。

2、定义数组P[length],其中P[i]表示以Ti为中心的最长回文长度的一半(因为T添加了字符’#‘,故其一般即为S中最长回文子串,也即Ti最长边界到Ti的距离)

如:

T = # a # b # a # a # b # a #

P = 0 1 0 3 0 1 6 1 0 3 0 1 0

P中最长的长度为6,而6即为S的最长回文子串。显然,当长度为偶数时,在T中对应的是’#‘;为奇数时,对应的是原有字符,这也就是为何要添加附加字符的原因——统一奇偶情况。

下面问题的关键即为如何得到P数组。

假设现在已经遍历到i = 13的位置,原字符串S的最长回文长度在T中对应位置C = 11,对应原字符串S的长度为9,字符串为”abcbabcba“。C的两边界分别为L = 2,R = 20;i关于C对应的位置为i‘ = 9。现在如何确定P[i]的大小呢?

图中,i‘下方的绿色实线表明了关于i‘为中心的左右边界,又i‘边界被包含在L、R之间,故i下方的绿色实线区域也一定是对称的。(即i‘、i以C对称,i‘为中心的长度边缘小于边界L、R,则P[i] = P[i‘]),令P[i] = p[i‘] = 1。

下面讨论另一种情况(即i‘、i以C对称,i‘为中心的长度边缘超出边界L或R),如下图

假设现在已经遍历到i = 15的位置,原字符串S的最长回文长度在T中对应位置C = 11,对应原字符串S的长度为9,字符串为”abcbabcba“。C的两边界分别为L = 2,R = 20;i关于C对应的位置为i‘ = 7。

图中绿色实线表示i、i‘为中心在边界L、R内一定对称的区域;红色实线表示超出边界L、R可能无法保证i对称的区域;绿色虚线表示横跨C的区域。

显然,我们只能判定绿色实线部分一定对称,即P[i] >= 5,但余下部分是否匹配我们则需要一一比较。

综上,得出以下结论:

if P[ i’ ] ≤ R – i,
then P[ i ] ← P[ i’ ]
else P[ i ] ≥ P[ i’ ]. (Which we have to expand past the right edge (R) to find P[ i ].

剩下一步更新C的位置则变得容易,当当前位置i的边界超过原有R则需要更新,即

if P[i] + i > R

C = i;

R = i + P[i].

class Solution
{
	public:    //88 / 88 test cases passed.    //Runtime: 18 ms
	string longestPalindrome(string s)
	{
		if (s.size() < 2)
		{
			return s;
		}                //将字符串变为需要的形式
		string pStr;
		for (int i = 0; i < s.size(); ++i)
		{
			pStr.push_back(‘#‘);
			pStr.push_back(s[i]);
		}
		pStr.push_back(‘#‘);                //寻找回文
		int length = (int)pStr.size();
		int *arrIndex = new int[length];//每个下标对应的回文长度,P
		int mid = 0;//回文子串的中心位置,C
		int mx = 0;//回文子串的边界,R
		int maxIndex = 0;
		int maxLength = 0;
		for (int i = 0; i < length; ++i)
		{
			arrIndex[i] = 0;
			int mirror = (mid << 1) - i;//i关于中心的对称位置,i‘
			if (mx > i)
			{//是否超出边界
				arrIndex[i] = (arrIndex[mirror] < mx - i) ? arrIndex[i] : mx - i;
			}
			while ((i + arrIndex[i] + 1) < length && (i - arrIndex[i] - 1) >= 0 && pStr[i + arrIndex[i] + 1] == pStr[i - arrIndex[i] - 1])
			{
				++arrIndex[i];
			}
			if (arrIndex[i] + i > mx)
			{//如果边缘超出了当前,需要更新
				mid = i;
				mx = i + arrIndex[i];
			}
			if (arrIndex[i] > maxLength)
			{
				maxLength = arrIndex[i];
				maxIndex = (i - 1) >> 1;
			}
		}
		delete[] arrIndex;
		string outputStr(s, maxIndex - (maxLength - 1)/2, maxLength);
		return outputStr;
	}
};

  

原文地址:https://www.cnblogs.com/wuyepeng/p/9735825.html

时间: 2024-10-05 00:30:22

寻找最长回文字符串的相关文章

如何寻找最长回文子串

回文串是面试常常遇到的问题(虽然问题本身没啥意义),本文就告诉你回文串问题的核心思想是什么. 首先,明确一下什:回文串就是正着读和反着读都一样的字符串. 比如说字符串 aba 和 abba 都是回文串,因为它们对称,反过来还是和本身一样.反之,字符串 abac 就不是回文串. 可以看到回文串的的长度可能是奇数,也可能是偶数,这就添加了回文串问题的难度,解决该类问题的核心是双指针.下面就通过一道最长回文子串的问题来具体理解一下回文串问题: string longestPalindrome(stri

最长回文字符串 POJ3974

曾经有一个好算法放到我面前,我没有好好珍惜,直到用到的时候才后悔莫及. 那就是Manacher(马拉车算法),以O(n)的复杂度计算最长回文字符串. 曾经刷Leetcode的时候,室友跟我说了这个算法,但当时那个题目用中间枚举也过了,我就没有在意,直到前天才弄会,写这篇报告之前, 我又专门写了一遍马拉车,果然还是有点问题的. 详细原理链接 点击 Mark #include <stdio.h> #include <iostream> #include <string.h>

求取最长回文字符串,o(n)的最优算法manacher

算法的第一步就是在每个字符的左右都加上一个#,这样有什么效果呢. 比如aba初始化之后为#a#b#a#,字符串长度为7是奇数. 比如1221初始化之后为#1#2#2#1#,字符串长度为9是奇数. 为什么我们要将其转换成奇数呢,因为算法求取回文串长度的时候,需要有一个中心节点,之后分别向左右搜索,所以需要将回文串豆转换为奇数长度. 之后我们需要将str[0]赋值为一个字符,可以赋值为$,不这样做也可以,但是这样做我们就可以从1开始处理字符串,大家知道C++的数组是从0开始的. 算法的第二步就进入了

csu-1328 近似回文词 和 最长回文字符串

原博文地址:http://blog.csdn.net/u012773338/article/details/39857997 最长回文子串 描述:输入一个字符串,求出其中最长的回文子串.子串的含义是:在原串连续出现的字符 串片段.回文的含义是:正着看和倒着看是相同的,如abba和abbebba.在判断是要求忽略所有的标点和空格,且忽略大小写,但输出时按原样输出(首 尾不要输出多余的字符串).输入字符串长度大于等于1小于等于5000,且单独占一行(如果有多组答案,输出第一组). 输入 :每行有一个

Java Longest Palindromic Substring(最长回文字符串)

假设一个字符串从左向右写和从右向左写是一样的,这种字符串就叫做palindromic string.如aba,或者abba.本题是这种,给定输入一个字符串.要求输出一个子串,使得子串是最长的padromic string. 下边提供3种思路 1.两側比較法 以abba这样一个字符串为例来看,abba中,一共同拥有偶数个字.第1位=倒数第1位.第2位=倒数第2位......第N位=倒数第N位 以aba这样一个字符串为例来看,aba中.一共同拥有奇数个字符.排除掉正中间的那个字符后,第1位=倒数第1

查找字符串中的最长回文字符串---Manacher算法

转载:https://www.felix021.com/blog/read.php?2040 首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号.比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#. 为了进一步减少编码的复杂度,可以在字符串的开始和结尾加入另一个特殊字符这样就不用特殊处理越界问题,比如%#a#b#a#@;(如果是C++,字符串末尾有一个\0,故结尾处不需要添加额为的特殊字符@) 然后用一个数组

hdu3068 求一个字符串中最长回文字符串的长度 Manacher算法

最长回文 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 31611    Accepted Submission(s): 11618 Problem Description 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.回文就是正反读都是一样的字符串,如aba, abba等 Input 输入有多组

URAL 1297 求最长回文字符串

有种简单的方法,数组从左到右扫一遍,每次以当前的点为中心,只要左右相等就往左右走,这算出来的回文字符串是奇数长度的 还有偶数长度的回文字符串就是以当前扫到的点和它左边的点作为中心,然后往左右扫 这是O(n^2)的复杂度,这道题过还是没有问题的 这里我主要练习的是另外的利用后缀数组加RMQ算法来解决这个问题 大致思想跟上面一致 首先将字符串反转贴在原字符串末尾,将字符通过ASCII码转化为字符,之间用一个1分开,最后贴一个0 然后得到它的后缀数组以及height[]数组 那么找回文字符也是扫一遍,

字符串中的最长回文字符串

“”“ 形如 abba,abaaba正反序相等的字符串称之为回文字符串 题源:领口 ”“” 1.用键值的形式去记录已经存在的字符和其序列 2.遍历原始的字符串 如果字典中没有则存入  如果有 则对其进行回文检测 3.若出现回文字符串,与现在的进行比较 #但是 单单以键值对形式存储 字符和序列  如原始字符串为 ‘abcacbae’ ,和‘abcaddedda’ 这种字符串 那么原先设计的程序就会崩溃 #要找到一个 判别当前字符串的在原字符串中的有效起始位置. kill:通过 键 列表 形式的键值