[LeetCode] Longest Palindromic Substring 解题报告

—— write for my baby, mua


[题目]

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length ofS is 1000, and there exists one unique longest palindromic substring.


[中文翻译]

给定一个字符串S,求S的最长回文字串。输入保证S的长度小于等于1000,且只有一个唯一的最长回文字串。


[解题思路]

回文子串的长度可能是奇数也可能是偶数,这里有一个tips,在原串头尾和每两个字母间插入‘#’,求新串的最长回文子串,去掉‘#’之后的串即为原串的最长回文子串。

新串的最长回文子串长度必为奇数。

事实上,插入的符号‘#’不需要是特殊字符,任意的字符(包括原串中出现的字符)都是可以的。

以下的算法均为求新串的最长回文子串。


O(KN) K=len(longest palindromic substring)

依次遍历字符串,将遍历到的字符作为回文的中间字符,向两边扩展,直到不满足回文条件。记录当前最长子串的中间字符下标和扩展长度,在遍历的过程中更新。


O(kN) k<len(longest palindromic substring)

根据回文的性质,在遍历的过程中,计算当前字符为中心的最长回文串时,可利用之前的计算结果,如下图。


图1. 利用之前计算结果示意图

L1和L2分别是以index和j为中心的最长回文串。i是当前遍历到的中心字符,j是i关于index的对称点。根据回文的性质,则L3必定是一个回文串。

下面,进行详细地讨论L1确定的情况下的各种情况。

一、 i处于L1之外


图2. 情况一-i处于L1之外

对于i处于L1之外的情况,不能利用j处的信息,因此i处最长回文需要从中心开始扩展。

二、 i处于L1内

对于i处于L1内的情况,分三种情况讨论。

    1. L2⊊L1


图3. 情况二-i处于L1内-1-L2⊊L1

L2⊊L1,因为L2是j处的最长回文子串,则i处最长回文子串的长度即为L2的长度。

    2. L2⊂L1,且L2的左边界正好为L1的左边界


图4. 情况二-i处于L1内-2-L2⊂L1且L1、L2左边界重合

L3实线部分与L2对称,必定是回文子串,但因为右边黑色格子的信息未知,所以L3实线部分未必是最长回文子串。因此可以从L3实线部分开始扩展。

    3. L2-L1≠∅


图5. 情况二-i处于L1内-2-(L2-L1≠∅)

当L2的左边界小于L1时,i处的最长回文子串必定为L3,因为右边蓝色格子一定不等于左边橙色格子,否则L1就不是index处的最长回文子串了。


在程序中,可以用一个数组max[i]记录以i为中心的最长回文子串单边长度,来保存L2的信息。index和L1可用两个整数index和单边长度mx表示。


现在,需要考虑的是L1如何确定。有两个策略。

1. 以当前出现过的最长回文子串作为L1。

2. 以当前出现过的右边界最大的回文子串作为L1。

这两个策略都不是最优策略。因为我们的目标是尽可能地利用之前的信息,而这两个策略都存在例子不能充分地利用信息。一般情况下,策略2较优。


最后找到最长回文子串后,需要把预处理的‘#’去掉。


[C++代码]

class Solution {
public:
	string longestPalindrome(string s) {
		string ss = "#";
		int max[2005];
		int index, mx;

		for (int i = 0; i < s.size(); i++)
			ss = ss + s.at(i) + "#";

		max[0] = 1;
		index = 0; mx = 1;

		for (int i = 1; i < ss.size(); i++) {
			int j = index + index - i;
			int leftBound = index - mx;

			if (j <= leftBound) {
				max[i] = 1;
				int kl = i - 1, kr = i + 1;
				while (kl>=0 && kr < ss.size() && ss.at(kl) == ss.at(kr)) {
					max[i]++;
					kl--; kr++;
				}
			}
			else {
				if ((j - max[j]) > leftBound)
					max[i] = max[j];
				else if ((j - max[j]) == leftBound) {
					max[i] = max[j];
					int kl = i - max[i], kr = i + max[i];
					while (kl >= 0 && kr < ss.size() && ss.at(kl) == ss.at(kr)) {
						max[i]++;
						kl--; kr++;
					}
				}
				else
					max[i] = index + mx - i;
			}

			if (i + max[i] >= index + mx) {
				index = i;
				mx = max[i];
			}
		}

		index = 0; mx = 1;
		for (int i=1; i<ss.size(); i++)
			if (max[i] > mx) {
				index = i;
				mx = max[i];
			}

		int l = index - mx + 2;
		int r = index + mx;
		string res = "";

		while (l < r) {
			res += ss.at(l);
			l += 2;
		}

		return res;
	}
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值