求最长回文串
Posted x-huang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了求最长回文串相关的知识,希望对你有一定的参考价值。
题目描述如下:
能立刻想到的就是爆破,先试试无脑爆破,时间复杂度O(n^3),然后毫无意外的超时了,爆破代码如下:
public String longestPalindrome(String s) { int max = 0; int length = s.length(); // 字符串长度只有1个或为空,那么它自身就是回文串 if (length <= 1) { return s; } String rtString = ""; // 从第一个字符开始,统计以每个字符开头的能达到的最大回文子串 for (int i = 0; i < length; i++) { for (int j = i + 1; j < length; j++) { String subStr = s.substring(i, j + 1); int left = 0; int right = subStr.length() - 1; boolean flag = true; // 判断是不是回文子串 while (left <= right) { if (subStr.charAt(left) == subStr.charAt(right)) { left++; right--; }else { flag = false; break; } } int size = subStr.length(); // 进行最大长度记录 if (flag && size > max) { max = size; rtString = subStr; } } } if (max == 0) { rtString = s.charAt(0) + ""; } return rtString; }
然后思考怎么使用动态规划,无奈本人动态规划渣渣,于是看题解
给出定义如下
那么 P(i, j)=(P(i+1,j−1) and Si?==Sj?) P(i,i)=true P(i, i+1) = ( S_i == S_{i+1} )P(i,i+1)=(Si?==Si+1?)
恍然大悟,然后开始编码,动态规划算法代码如下:
public String longestPalindrome1(String s) { if (s.length() <= 1) { return s; } int length = s.length(); boolean[][] dp = new boolean[length][length]; // 初始化单一字符为回文串 for (int i = 0; i < dp.length; i++) { dp[i][i] = true; } // 因为dp[i][j]取决于d[i+1][j-1]的状态,所以自底向上进行动态规划 for (int i = length - 1; i >= 0; i--) { for (int j = i; j < dp[i].length; j++) { if (j - i < 2) { //两个字符相邻或者就是同一个字符 dp[i][j] = (s.charAt(i) == s.charAt(j)); }else { // 字符 i~j 是否为回文取决于 i+1 ~ j-1是否为回文 及 s[i] 是否等于 s[j] dp[i][j] = (dp[i + 1][j - 1] && s.charAt(i) == s.charAt(j)); } } } // 遍历dp数组找到最长回文串 int maxlength = 0; int maxi = 0; int maxy = 0; for (int i = 0; i < dp.length; i++) { for (int j = i; j < dp.length; j++) { if (dp[i][j]) { int len = j - i + 1; if (len > maxlength) { maxlength = len; maxi = i; maxy = j; } } } } return s.substring(maxi, maxy + 1); }
继续看看还有什么算法,看到中心扩展法,即以某个字符为中心向两边扩展,时间复杂度为O(n^2)代码如下:
public String longestPalindrome2(String s) { int length = s.length(); if (length <= 1) { return s; } int max = 0; int startIndex = 0; // 这轮循环时计算 for (int i = 0; i < length; i++) { int left = i; int right = i; // 求得回文串长度为奇数的串,返回值为{回文串串长度,回文串开始下标} int[] jiCuan = getCuan(s, left, right); // 求得回文串长度为偶数的串,返回值为{回文串串长度,回文串开始下标} int[] ouCuan = getCuan(s, left, right + 1); int bigger = 0; if (jiCuan[0] > ouCuan[0]) { bigger = jiCuan[0]; left = jiCuan[1]; }else { bigger = ouCuan[0]; left = ouCuan[1]; } if (bigger > max) { max = bigger; startIndex = left; } } return s.substring(startIndex, startIndex + max); } public int[] getCuan(String s, int left, int right) { // s[left](奇数回文串时)或s[left]~s[right](偶数回文串时)为中心向两边扩展 while (left >= 0 && right < s.length()) { if (s.charAt(left) == s.charAt(right)) { left--; right++; }else { break; } } // 循环退出之前会把left-1,right+1,所以分别要加回来和减出去 left = left + 1; right = right - 1; int[] a = {right - left + 1, left}; return a; }
还有一种算法,比较难想到,时间复杂度为O(n),人称“马拉车算法”,这里给出参考链接:
https://www.cnblogs.com/grandyang/p/4475985.html
以上是关于求最长回文串的主要内容,如果未能解决你的问题,请参考以下文章