《剑指offer》输出最长回文子串

来源:互联网 发布:涉密软件开发资质 编辑:程序博客网 时间:2024/06/05 07:25

题目:求字符串的最长回文子串。(说在前面的话:该题目实际来自于leetcode,由于本人目前主要刷剑指offer在,而牛客网剑指offer上面貌似没有该题目,而本人又觉得该题目不错,收录于此,并且尽力写的详实,与此与大家分享,希望对大家有所帮助)

温馨提示:寻求快速解答的童鞋们可以直接跳过扯蛋的解析一,去看解析二。

解析一:正如伟大的总设计师邓小平所说:“无论白猫还是黑猫,抓到老鼠就是好猫”。下面就讲解下该题目的其中一种解法,就是暴力的干了。直接求解该字符串的所有子串,每个子串都判断是否是回文,最后输出最大的回文子串。但是当我们躺在自己的编译器里面yy的时候,等你提交到leetcode上的时候,你就觉得杯具了,我了个去,超内存限制了。我的妈呀,我这是作了什么孽,这该是多大消耗啊,怎么办,看来此题的暴力法是夭折了。代码就不拿出来献丑了,下面介绍个比较“绅士”的解法。

解析二:该法叫做“中心扩展法”,类似于一个小破孩儿扔个石头在水塘里,然后水波以该石头为中心向四周扩散去。就像下面这样
这里写图片描述

那么如何用该思想来求解该最大回文子串呢?
首先来看我们根据原串abcbaxy输出的最大子串是个什么东东了?
输入:abcbaxy
输出:abcba
用肉眼一看就知道是这个abcba,那么该串是如何撸出来的呢?总不能是看出来的吧。
其实,我们可以利用中心扩展法求解该题,意思是以第i个节点为水波的中心点,然后向字符串的两端扩散,当左边的字符串和右边的字符串相同的时候就继续,然后,怎么继续?那就是右边的游标继续向右+1,左边的游标向左-1,如果左右的字符串相等,又继续这样干下去。最后判断该区间的回文字符串长度是否大于初始化的max(max初始化为0)的值,如果大于就保存该左右游标了,然后把当前区间的长度赋予max。最后根据获得的最大回文子串的左右游标获取子串就是最大了。讲到这,大家可能还有点模糊。下面就画图说明下。

注意:用“中心法”求解的时候需要分两种情况,第一种是回文子串是奇数位,第二种是回文子串是偶数位置。

第一种:回文是奇数位
这里写图片描述
上图是举例当i=2时候,左边的游标left=i-1,右边的游标right=i+1,当左边指向的字符b和右边的b相同的时候就left–,right++。如下图
这里写图片描述

当条件超过范围left>=0&&left<right&&right<s.lenght()就跳出
然后统计该区间的回文字符长度5是否大于max(max值初始化为0),如果大于,max=当前子串的长度5,然后保存当前的left以及right的值,方便以后计算最大的子串。

第二种:回文是偶数位
这里写图片描述
依然举例i=2,与奇数位回文不一样的是left=i,right=i+1,后面的计算原理同上。
当然talk is cheap,show me your code

/** * Created by wickedvalley on 2017/7/18. * 最长回文子串 */public class LongestPalindrome {    //中心扩展法:总共有两种情况。第一种字符串为奇数,第二种字符串为偶数。无需判断字串的奇偶性,因为我们保存的是最大的回文字符串    //abcba    //abccba    public static String longestPalindrome(String s) {        String  result="";        int leftIndex=0;        int rightIndex=0;        int max=0;        for(int i=0;i<s.length();i++){//子串为奇数            int left=i-1;//第i个字符串的左侧第一个字符            int right=i+1;//第i个字符串的右侧第一个字符            while (left>=0&&left<right&&right<s.length()){                if(s.charAt(left)==s.charAt(right)){//左侧与右侧相同                    left--;                    right++;                }else {//左侧与右侧不同的时候就跳出                    break;                }            }            ++left;//由于上边的循环都--了,该++了,恢复原来的位置            --right;//由于上边的循环最后都++了,该--了,恢复原来的位置            if((right-left+1)>=max){//该区间的回文长度大于最大的字符串长度,该记下相应的参数了                max=right-left+1;                leftIndex=left;                rightIndex=right;            }        }        for(int i=0;i<s.length();i++){//子串为偶数            int left=i;//左边就是该字符串了            int right=i+1;//右边第一个需要与left位置的比较            while (left>=0&&left<right&&right<s.length()){                if(s.charAt(left)==s.charAt(right)){                    left--;                    right++;                }else {                    break;                }            }            ++left;//理由均同上            --right;            if((right-left+1)>=max){                max=right-left+1;                leftIndex=left;                rightIndex=right;            }        }        result=s.substring(leftIndex,rightIndex+1);//需要注意,substring函数是左闭右开的,需要rightIndex+1        return  result;    }    public static void main(String[] args) {        System.out.println(longestPalindrome("abb"));//测试下    }}
原创粉丝点击