出局了
if(max0) {
出局了
while(max0) {
出局了
}
}
//free mem
delete [] C;
}
2、以下是直观的动规解。
从以下内容转述。 http://blog.163.com/Zhao hai _ 1988/blog/static/2095100852012716105847112 /
方法二动态规划时间复杂度O(N2 )、空间复杂度O(N2 ) ) )。
动态计划是暴力法的进化版,不需要对所有的子串重新计算来调查是否是回文。 我们可以记录几个需要的东西,在o(1)的时间里可以判断那个部分序列是否是回文。 这样比暴力法更能节约O(N )的时间复杂度哦,嘿嘿,其实优化很简单吧。
p(I,j )为1时,字符串si-SJ为回文,为0时,表示字符串si-SJ不是回文。
p(I,j )=p ) I1,j-1 ) ) S[i]=S[j]时。 这是动态规划的状态转移方程。
p(I,I )=1,p ) I,i 1)=if ) s[I]=s[I1] ) ) ) ) ) ) ) ) )。
stringlongestpalindromedp{
intn=s.length (;
intlongestBegin=0;
intmaxLen=1;
booltable[1000][1000]={false};
for(inti=0; I
table[i][i]=true; //前期初始化
}
for(inti=0; i n-1; I ) {
if(s ) I )==s(I1 ) {
table[i][i 1]=true; //前期初始化
longestBegin=i;
maxLen=2;
}
}
for(Intlen=3; len=n; len ()。
for(inti=0; i n-len 1; I ) {
intj=i len-1;
if(s(I )==s ) j ) table (i1 ) ) j-1 ) {
table[i][j]=true;
longestBegin=i;
maxLen=len;
}
}
}
returns.substr(LongestBegin,maxLen );
}
方法三中心扩张法
这个算法的思想其实很简单呢。 时间复杂度只有o(n2 ),空间复杂度只有o(n2)。 对于给定的字符串s,以该字符串s中的各字符c为中心向两侧扩展,记录以字符c为中心的回文部分列的长度。 但是,需要注意的是,回文的情况有时是a b a,有时是兴奋的羊。
stringexpandaroundcenter (strings、intc1、intc2 ) {
intl=c1,r=c2;
intn=s.length (;
wile(l=0r=n-1s[l]==s[r] ) ) )
L----;
r;
}
returns.substr(L1,r-l-1 );
}
stringlongestpalindromesimple (字符串)。
intn=s.length (;
if(n==0)返回' );
string longest=s.substr (0,1 ); //asinglecharitselfisapalindrome
for(inti=0; i n-1; I ) {
string P1=扩展负载中心(s,I,I );
if(P1.Length ) (longest.length ) ) )
长时间=P1;
stringp2=扩展负载中心(s,I,i 1);
if(P2.Length ) (longest.length ) ) )
长时间=p2;
}
返回长度;
}
方法四传说中的Manacher算法。 时间复杂度o(n ) )
该算法有其巧妙之处,统一考虑了奇数回文串和偶数回文串。 这一点一直是做回文列问题时很麻烦的地方。 该算法的另一个好处是充分利用字符匹配的特殊性,避免了大量不必要的重复匹配。
>算法大致过程是这样。先在每两个相邻字符中间插入一个分隔符,当然这个分隔符要在原串中没有出现过。一般可以用‘#’分隔。这样就非常巧妙的将奇数长度回文串与偶数长度回文串统一起来考虑了(见下面的一个例子,回文串长度全为奇数了),然后用一个辅助数组P记录以每个字符为中心的最长回文串的信息。P[id]记录的是以字符str[id]为中心的最长回文串,当以str[id]为第一个字符,这个最长回文串向右延伸了P[id]个字符。原串: w aa bwsw f d
新串: # w# a # a #b# w # s # w #f # d # 辅助数组P:1 2 1 2 3 2 1 2 1 2 1 4 1 2 1 2 1 2 1
这里有一个很好的性质,P[id]-1就是该回文子串在原串中的长度(包括‘#’)。如果这里不是特别清楚,可以自己拿出纸来画一画,自己体会体会。当然这里可能每个人写法不尽相同,不过我想大致思路应该是一样的吧。
现在的关键问题就在于怎么在O(n)时间复杂度内求出P数组了。只要把这个P数组求出来,最长回文子串就可以直接扫一遍得出来了。
那么怎么计算P[i]呢?该算法增加两个辅助变量(其实一个就够了,两个更清晰)id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。 然后可以得到一个非常神奇的结论,这个算法的关键点就在这里了:如果mx > i,那么
P[i] >= MIN(P[2 * id - i], mx - i)。就是这个串卡了我非常久。实际上如果把它写得复杂一点,理解起来会简单很多:
//记j = 2 * id - i,也就是说 j 是 i 关于 id 的对称点。
if (mx - i > P[j])
P[i] = P[j];
else /* P[j] >= mx - i */
P[i] = mx - i; // P[i] >= mx - i,取最小值,之后再匹配更新。
当 mx - i > P[j] 的时候,以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于 i 和 j 对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有 P[i] = P[j]。
当 P[j] > mx - i 的时候,以S[j]为中心的回文子串不完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] >= mx - i。至于mx之后的部分是否对称,就只能老老实实去匹配了。
由于这个算法是线性从前往后扫的。那么当我们准备求P[i]的时候,i以前的P[j]我们是已经得到了的。我们用mx记在i之前的回文串中,延伸至最右端的位置。同时用id这个变量记下取得这个最优mx时的id值。(注:为了防止字符比较的时候越界,我在这个加了‘#’的字符串之前还加了另一个特殊字符‘$’,故我的新串下标是从1开始的)
#include
#include
usingnamespacestd;
constintN=300010;
intn,p[N];
chars[N],str[N];
#define_min(x,y)((x)y)?(x):(y))
voidkp()
{
inti;
intmx=0;
intid;
for(i=n;str[i]!=0;i++)
str[i]=0;//没有这一句有问题。。就过不了ural1297,比如数据:ababa aba
for(i=1;i
{
if(mx>i)
p[i]=_min(p[2*id-i],p[id]+id-i);
else
p[i]=1;
for(;str[i+p[i]]==str[i-p[i]];p[i]++)
;
if(p[i]+i>mx)
{
mx=p[i]+i;
id=i;
}
}
}
voidinit()
{
inti,j,k;
str[0]='$';
str[1]='#';
for(i=0;i
{
str[i*2+2]=s[i];
str[i*2+3]='#';
}
n=n*2+2;
s[n]=0;
}
intmain()
{
inti,ans;
while(scanf("%s",s)!=EOF)
{
n=strlen(s);
init();
kp();
ans=0;
for(i=0;i
if(p[i]>ans)
ans=p[i];
printf("%dn",ans-1);
}
return0;
}
if( mx > i)
p[i]=MIN( p[2*id-i], mx-i);
就是当前面比较的最远长度mx>i的时候,P[i]有一个最小值。这个算法的核心思想就在这里,为什么P数组满足这样一个性质呢?
(下面的部分为图片形式)
LEETCODE上也有这个题的详细说明,不过是英文版本的。
http://www.leetcode.com/2011/11/longest-palindromic-substring-part-ii.html