最长回文串

来源:互联网 发布:专业淘宝拍摄 编辑:程序博客网 时间:2024/05/01 14:45

              (1)思路

                        1. 因为字符串长度为奇数和偶数时处理方式不同,所以这里可以使用插入特殊字符的方法,使得每个字符串在处理之后长度都为奇数。处理方法为:在任意两个字符之间和字符串的开始和结尾插入'#'。(因为设字符为n个,那么共有n-1个间隙,n+(n-1)必然为奇数,再加上首尾'#'还是奇数)。因为要防止越界,所以在修改后的字符串首尾再加上边界,开始为'$',结尾为'@'

private String chuli(String str) {StringBuffer sb = new StringBuffer();sb.append('$');                   //加入开始标识for(int i=0; i<str.length(); i++){sb.append('#');           //每个字符之前加入#sb.append(str.charAt(i));}sb.append('#');                  //末尾加入#sb.append('@');                  //加入结束符return sb.toString();}
                        测试:

String str = "abba";str = chuli(str);for(int i=0; i<str.length(); i++)System.out.print(str.charAt(i)+" ");

                       结果为:

$ # a # b # b # a # @ 
                        2. 核心算法

                           1>定义数组p[i]:表示以str[i]为中心的最大回文串的长度(包含字符str[i]),pi表示已经求得的拥有最长回文串的字符的下标,mx表示最长回文串所能达到的最右边的边界
                           2>

                                

                             pi是最长回文串的中心(淡蓝色),如果以j为中心的最大回文串如上图所示,那么处于i处的情况和j处相同(因为pi的两侧是对称的)。这样求i的时候就可以利用j的结果了,此处显然j = 2*pi-i

                             mx是最长回文串的右边界,因为i<mx所以在求p[i],即以i为中心的回文串时,就可以利用关于pi的对称点j的信息。因为图中蓝色标志长度相同,均为mx-i,所以当mx-i>p[j],即图中所示情况时,p[i] = p[j],其余能不能再延长,就只能一个个字符判断了。

                           3>

                            

                            这种情况虽然mx>i,但是mx-i<p[j],也就是说最长回文串并没有全部包含以j为中心的回文串,所以此处最长只能选择mx-i为半径,即p[i] = mx-i

                           4>最后一种情况:mx-i<0,即i在最长回文串的外面,所以此时就只能从头开始算了,即p[i] = 1


              (2)代码

@Testpublic void huiwen(){String str = "abba";str = chuli(str);int mx = 0;int pi = 1;int[] p = new int[str.length()];for(int i=1; i<str.length(); i++)  //因为已经将字符串第0个字符设置为$,所以直接从第1个开始{if(mx>i)                  //如果i在mx内,即可以利用以前求得的结果{if(mx-i>p[pi*2-i])             //整个i的对称点j = pi*2-i,都在最长回文串中p[i] = p[pi*2-i];else                           //一部分在最长回文串外p[i] = mx-i;}else                     //因为i在mx外,所以不能利用之前求得的结果,那么就直接设为1p[i] = 1;while(i-p[i]>0 && i+p[i]<str.length() && str.charAt(i+p[i])==str.charAt(i-p[i]))  //设置完p[i]的初始值之后,再一次扩展两边,看看是                                                                                                          //否能够将回文串扩大p[i]++;if(mx<i+p[i])              //如果当前求得的回文串长度大于之前最大的,那么更新最大值{mx = i+p[i]-1;    //i+p[i]-1为右边界pi = i;}}int maxLen = p[0];for(int i=1; i<p.length; i++)              //选出其中最长的半径maxLen = Math.max(maxLen, p[i]);System.out.println(maxLen-1);              //maxLen-1就是原字符串的最大回文串长度,下面证明}

              (3)注意

                        1.因为p[i]记录的是插入分隔符之后的回文串的半径,所以以i为中心的回文串的长度为2*p[i]-1.如bb->$#b#b#@,中间#的半径为3,所以回文串长度为2*3-1 = 5

                        2.因为#b#b#长度-1后,就正好是原来字符串长度的2倍,即((2*p[i]-1)-1)/2 = p[i]-1,得证。


0 0
原创粉丝点击