最长回文子串
来源:互联网 发布:全程软件测试 朱少民 编辑:程序博客网 时间:2024/06/07 00:19
转自:http://my.oschina.net/pathenon/blog/63575
问题描述:
给定一个字符串S=A1A2...An,要求找出其最长回文子串(Longest Palindromic Substring)。所谓回文子串就是S的某个子串Ai...Aj为回文。例如,对字符串S=abcdcbeba,它的回文子串有:bcdcb,cdc,beb,满足题目要求的最长回文子串为bcdcb。
推理思路:
1.由于回文可能由奇数个字符组成,也可能由偶数个字符组成。对奇数回文的处理比较直观,只需要以某个字符为中心,依次向两边扩展即可。因此,我们可以通过如下方式把对偶数回文的处理转换成对奇数回文的处理:在字符边界添加特殊符号。例如,对字符串aba,预处理后变成#a#b#a#;对字符串abba,预处理后变成#a#b#b#a。可以看出,不管是奇数回文,还是偶数回文,在与处理后都变成奇数回文。在找出与预处理后字符串的最长回文后,只需要去除所有的#即为源字符串的最长回文。
2.对寻找字符串某类子串的问题,最简单直观的想法就是穷举出所有子串一一进行判别。这里也不例外,当然时间复杂度也很高,为O(n^3)。
3.对该问题,我们可以进行一定程度的简化处理。既然回文是一种特殊的字符串,我们可以以源字符串的每个字符为中心,依次寻找出最长回文子串P0, P1,...,Pn。这些最长回文子串中的最长串Pi = max(P1, P2,...,Pn)即为所求。请看源码:
01
string find_lps_native(
const
string &str)
02
{
03
int
center = 0, max_len = 0;
04
for
(
int
i = 1; i < str.length()-1; ++i)
05
{
06
int
j = 1;
07
08
//以str[i]为中心,依次向两边扩展,寻找最长回文Pi
09
while
(i+j < str.length() && i-j >= 0 && str[i+j] == str[i-j])
10
++j;
11
--j;
12
if
(j > 1 && j > max_len)
13
{
14
center = i;
15
max_len = j;
16
}
17
}
18
19
return
str.substr(center-max_len, (max_len << 1) + 1);
20
}
举例说明:对字符串S=abcdcba而言,最长回文子串是以d为中心,半径为3的子串。当我们采用上面的做法分别求出以S[1]=a, S[2]=b, S[3]=c, S[4]=d为中心的最长回文子串后,对S[5]=c,S[6]=b...还需要一一进行扩展求吗?答案是NO。因为我们已经找到以d为中心,半径为3的回文了,S[5]与S[3],S[6]与S[2]...,以S[4]为对称中心。因此,在以S[5],S[6]为中心扩展找回文串时,可以利用已经找到的S[3],S[2]的相关信息直接进行一定步长的偏移,这样就减少了比较的次数(回想一下KMP中next数组的思想)。优化的思想找到了,我们先看代码:
01
string find_lps_advance(
const
string &str)
02
{
03
//find radius of all characters
04
vector<
int
> p(str.length(), 0);
05
int
idx = 1, max = 0;
06
for
(
int
i = 1; i < str.length()-1; ++i)
07
{
08
if
(max > i)
09
{
10
p[i] = p[(idx << 1) - i] < (max - i) ? p[(idx << 1) - i]:(max - i);
11
}
12
while
(str[i+p[i]+1] == str[i-p[i]-1])//这个地方需要一个越界判断
13
p[i] += 1;
14
if
(i + p[i] > max)
15
{
16
idx = i;
17
max = i+p[i];
18
}
19
}
20
21
// find the character which has max radius
22
int
center = 0, radius = 0;
23
for
(
int
i = 0; i < p.size(); ++i)
24
{
25
if
(p[i] > radius)
26
{
27
center = i;
28
radius = p[i];
29
}
30
}
31
32
return
str.substr(center-radius, (radius << 1) + 1);
33
}
p:以S[i]为中心的最长回文串的半径为p[i]。
idx:已经找出的最长回文子串的中心位置。
max:已经找出的最长回文子串的结束位置。
算法的主要思想是:先找出所有的p[i],最大的p[i]即为所求。在求p[j] (j>i)时,利用已经求出的p[i]减少比较次数。
代码中比较关键的一句是:
1
p[i] = p[(idx << 1) - i] < (max - i) ? p[(idx << 1) - i]:(max - i);
在求p[i]时,如果max>i,则表明已经求出的最长回文中包含了p[i],那么与p[i]关于idx对称的p[ (idx << 1) - i]的最长回文子串可以提供一定的信息。看了两幅图大概就明白什么意思了:
求二者的最小值是因为当前能够获取的信息都来自max的左侧,需要进一步比较,求出以S[i]为中心的最长回文串。
5.除了上述的几种做法外,还可以利用动态规划以及后缀树来进行求解。下次进行介绍。
结束行文之前,补充一句,对于字符串类的问题,建议多画一画,寻找其中的规律。
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 最长回文子串
- 指针到底是什么
- 查看一个数是不是2的n次方
- cuda数组内存分配
- Java多线程设计模式之线程池模式
- 数据结构与算法学习笔记——堆排序
- 最长回文子串
- Android测试驱动开发实践1
- 指针的定义及运算
- 日历分析(二) java中的GUI
- 实例:Easyui的combobox实现动态数据级联
- 使用typeid(变量或类型).name()来获取常量或变量的类型---gyy整理
- 输入法
- mysql 存储引擎简介和选择
- 充实的一上午