题目
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例2:
输入: “cbbd”
输出: "bb“
详解:
在完成算法的编程时,设计算法是至关重要的,虽然来自数理系,但是自己在编写算法时还是不能设计出时间复杂度比较小的方案,在网上了解到“Manacher算法”的时间复杂度达到了O(n),由于学习起来比较困难,我将其简化,时间复杂度为O(n*n)。下面通过举例子来解释:
eg.找出“aababbaa”的最长回文数组
解:(先来个大致介绍) 本文采用的算法其实是寻找一个中心点然后依次比较其左右两边的元素,直到发现左右两边的元素不相同为止时,得到的数组就是一个回文数组。
缺点:1.中心点的选取是依次选取,并没有采取优化 2.不能识别偶回文数组,形如“abba”
针对以上两个缺点,其实第二个是可以规避的,我们只需要在数组中加入肯定不会出现的元素,在这里我们加入“#”:
newS: #a#a#b#a#b#b#a#a# (长度为2s.length+1)
然后引入数组Len[2s.length+1],其中Len[i]储存的元素是newS中以第i个元素为中心的最长回文数组的半径+1,即:
newS: #a#a#b#a#b#b#a#a# (长度为2*s.length+1)
Len : 12321414141252121
那么问题来了,Len中的元素要怎么算?简单,设要求的回文数组左端点为left,右端点为right,right从中心元素newS[i]右面第一开始取,即初始时right=i+1,此时left=right-2i。然后判断newS[left]与newS[right]是否相同,若相同right+1,left也相应改变,直到newS[left]与newS[right]不相等为止。
下面贴出代码:
public class Solution {
public string LongestPalindrome(string s) {
int left=0;
int right=0;//先判断s的特殊情况(为空、长度为1) if(string.IsNullOrEmpty(s)) {return string.Empty;} if(s.Length==1) {return s;} //构造newS int Lens=s.Length; int newLen=2*Lens+1; char[] newS=new char [newLen]; for(int i=0;i<Lens;i++) { newS[2*i]='#'; newS[2*i+1]=s[i]; } newS[2*Lens]='#'; //求数组Len[] int[] Len=new int[newLen]; for(int i=0;i<newLen;i++) { right=i+1; left=2*i-right; while(left>=0&&right<newLen&&newS[left]==newS[right]) { right++; left=2*i-right; Len[i]=right-i;//回文数组半径 } } //求最长回文子串半径 int index=0; for(int i=0;i<newLen;i++) { index=Len[index]>Len[i]?index:i; } //返回最长回文子串本串 String maxlong=""; for(int i=index+1-Len[index];i<index+Len[index];i++) { if(newS[i]=='#'){maxlong+="";} else{maxlong+=newS[i];} } return maxlong; } }
自我评价:
这段代码写的效率不是很高,建议读者看懂后可以参考下文:https://zhuanlan.zhihu.com/p/40559493 里面有Mancher算法的源代码。
实验结果:
执行用时 :172 ms, 在所有 C# 提交中击败了64.53%的用户
内存消耗 :25 MB, 在所有 C#提交中击败了16.30%的用户