Leetcode5:Longest Palindromic Substring

题目描述

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: "cbbd"
Output: "bb"

分析

最长回文子串,使用Manacher’s Algorithm可在线性时间内求解。算法的详细讲解和证明读者可以自行Google,这里只是讲一下具体的实现。

首先需要预处理输入字符串,在每个字符的前后加上一个相同的字符,在字符串的最前面再加上另一个不同的字符。比如输入为bob,那么预处理后的字符串可以是!#b#o#b#,一共8个字符。最前面的字符可以随意选取,中间的字符一般为#,当然也可以自由选取。然后需要一个数组用来记录以当前字符为中心的回文子串的半径,注意这里是半径。比如上面的bob例子,处理过后,第一个b的半径就应该为2。最开始这个数组初始化为0,然后需要用一个变量reid来存储最长回文子串的中心元素的下标,remx来存储最长回文子串的半径。注意这里所说的下标和半径都是针对处理过后的字符串而言,求出最后结果还需要进行处理。另外还需要两个变量,id用来存储当前遍历字符的下标,mx用来存储以当前字符为中心的回文子串的最右边字符的下标。上面四个变量都初始化为0。

初始化结束以后就可以开始遍历字符串中的每个字符了。用i表示当前字符的下标,首先需要求解p[i]。当mx > i时,p[i] = min(p[2*id-i],mx-i)。否则,p[i] = 1。这个公式的具体讲解和证明这里不再说明。然后开始循环求解p[i]的最大值,即判断temp[i-p[i]]是否等于temp[i+p[i]],temp为处理过后的字符串。如果相等,p[i]加1,需要注意边界条件。

接着开始更新变量的值。如果mx < i + p[i],则mx = i + p[i],id = i。如果remx < p[i],则remx = p[i],reid = i。这个公式是这个算法最重要的核心,如果能够理解尽量理解,不能理解也要硬记住。大概意思是,如果mx小于当前能够到达的最右边界,则更新mx为最右边界,id为当前下标。如果remx小于当前半径,则更新remx为当前半径,reid为当前下标。只要记住每个变量所表示的含义,记住这个式子应该不难。remx和reid记录的是结果,而mx和id主要是为了上一步求解p[i]的简化。其实Manacher’s Algorithm快就快在求解p[i]上面,所以mx和id这两个变量就是关键所在。最后结果子串的起始位置为(reid-remx)/2,长度为remx-1,这两个值针对的就是原始输入字符串了。

AC代码如下:

class Solution {
public:
    string longestPalindrome(string s) {
        string temp = "!#";//处理后的字符串
        int length = s.length();
        for(int i = 0; i < length; ++i)//字符串处理
        {
        	temp += s[i];
        	temp += '#';
		}
		length = temp.length();
		int* p = new int[length];//半径
		memset(p, 0, length*sizeof(int));//初始化为0
		int remx = 0, reid = 0, mx = 0, id = 0;
		
		for(int i = 0; i < length; ++i)
		{
			if(mx  > i)//求解p[i]
			{
				p[i] = min(p[2*id - i], mx - i);
			}
			else
			{
				p[i] = 1;
			}
			while((i - p[i] >= 0) && (temp[i + p[i]] == temp[i - p[i]]))//求p[i]的最大值
			{
				++p[i];
			}
			if(mx < i + p[i])//更新右边界
			{
				mx = i + p[i];
				id = i;
			}
			if(remx < p[i])//更新结果半径和中心下标
			{
				remx = p[i];
				reid = i;
			}
		}
		
		return s.substr((reid-remx)/2, remx-1);//返回结果子串,针对原始输入
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值