zoukankan      html  css  js  c++  java
  • Leetcode之最长回文子串

    问题描述

    给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

    示例 1:

    输入: "babad"
    输出: "bab"
    注意: "aba" 也是一个有效答案。

    示例 2:

    输入: "cbbd"
    输出: "bb"

    解法

    思路很简单,首先想到若情况为回文字符串奇数个字符时,中间的字符为轴。比较两边的字符是否相同。当回文串字符为偶数时,刚开始假想它以空气为轴,则找不到其他字符的下标。于是想到在每个字符包括两边都插入一个特殊字符比如(),这样构成的回文字符串一定是奇数长度。也就是一定存在中间的某个字符,使得两边以它(可能是特殊字符也可能原字符串中的字符)为轴,并且此字符串的两边都以结尾。

    public static void longestPalindrome(String s) {
    		//在每两个字符中间插入特殊字符,保证回文字符串一定是奇数长度
    		String str=s.replace("", "*");
    		int maxl=1;
    		int index=0;
    		//从左到右遍历,寻找以每一个字符为轴的最大长度
    		for(int i=1;i<str.length();i++) {
    			if(maxl<=2*Math.min(i,str.length()-i-1)+1) {//两边的长度可以超过最大长度
    				for(int j=1;;j++) {
    					if(j<=i&&j<=str.length()-i-1) {//两边的长度可以取到的值
    						if(str.charAt(i-j)!=str.charAt(i+j)) {//这一个字符不是回文串中的
    							int l=2*j-1;
    							if(maxl<l) {
    								maxl=l;
    								index=i;
    							}
    							break;
    						}
    					}else {//到头了都是回文串
    						int l=2*j-1;
    						if(maxl<l) {
    							maxl=l;
    							index=i;
    						}
    						break;
    					}
    				}
    			}
    		}
    		//得到字符串
    		StringBuilder sb=new StringBuilder();
    		for(int i=index+1-(maxl-1)/2;i<=index-1+(maxl-1)/2;i++) {
    			if(i%2!=0) {
    				sb.append(str.charAt(i));
    			}
    		}
        }
    

    结果

    官方解法

    方法一 动态规划

    使用P[i][j]表示Si到Sj是否是一个回文串。
    动态规划转移方程可以写为P[i][j]=P[i+1][j-1]&Si,即先看中间再加上首尾两端。
    边界条件:因为单个的字符为回文串,P[i][i]=true,两个相邻的相同字符为回文串if Si==Si+1则P[i][i+1]=true
    根据这个思路我自己写了一个代码。跑出来的效果很差。

    public static String longestPalindrome(String s) {
    		if(s==null||s.isEmpty())
    			return "";
    		boolean[][]P=new boolean[s.length()][s.length()];
    		//边界
    		int length=1;
    		int start=0,end=0;
    		for(int i=0;i<s.length()-1;i++) {
    			if(s.charAt(i)==s.charAt(i+1)) {
    				P[i][i+1]=true;
    				if(length<2) {
    					length=2;
    					start=i;
    					end=i+1;
    				}
    			}
    			P[i][i]=true;
    		}
    		P[s.length()-1][s.length()-1]=true;
    		//
    		for(int i=0;i<s.length()-2;i++) {
    			for(int j=0;j<s.length()-i-2;j++) {
    				if(P[j+1][j+i+1]&&s.charAt(j)==s.charAt(j+i+2)) {
    					P[j][i+j+2]=true;
    					int l=i+3;
    					if(length<l) {
    						length=l;
    						start=j;
    						end=i+j+2;
    					}
    				}
    			}
    		}
    		s=s.substring(start, end+1);
    		return s;
        }
    

    方法二 中心扩展

    方法二的本质即为:我们枚举所有的「回文中心」并尝试「扩展」,直到无法扩展为止,此时的回文串长度即为此「回文中心」下的最长回文串长度。这个算法思想与我的很相似。但是跑出来的效果比我好。

    class Solution {
        public String longestPalindrome(String s) {
            if (s == null || s.length() < 1) return "";
            int start = 0, end = 0;
            for (int i = 0; i < s.length(); i++) {
                int len1 = expandAroundCenter(s, i, i);
                int len2 = expandAroundCenter(s, i, i + 1);
                int len = Math.max(len1, len2);
                if (len > end - start) {
                    start = i - (len - 1) / 2;
                    end = i + len / 2;
                }
            }
            return s.substring(start, end + 1);
        }
    
        private int expandAroundCenter(String s, int left, int right) {
            int L = left, R = right;
            while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
                L--;
                R++;
            }
            return R - L - 1;
        }
    }
    

    方法三 Manacher算法

  • 相关阅读:
    《程序员修炼之道:从小工到专家》阅读笔记02
    第二阶段团队冲刺10
    第二阶段团队冲刺09
    周总结
    第二阶段团队冲刺08
    第二阶段团队冲刺07
    小A和小B和幽灵追两人(双向BFS)
    C. 真假亚瑟王(暴力)
    小A的柱状图(栈的应用,找左右边界)
    小A买彩票
  • 原文地址:https://www.cnblogs.com/code-fun/p/13641510.html
Copyright © 2011-2022 走看看