Version 1(wrong version):
对。。。我就是先来搞笑一下的,这题是找最大的回文子串,我脑子里最先想到的是暴力解法还有动态规划,奈何动态规划对我来说太高端,先强行写个暴力work下试试,结局。。。是不美好的。
暴力先是出现了内存受限的问题,修改后出现了时间受限的问题,以下代码是修改后的,虽然没有AC,但是对于比较弱的我,也是我写的代码,将就记录一下吧。
暴力方法就是遍历所有的子串,检查是否回文串,遍历需要两层循环,而其实检查是否回文还需要一层循环,所以实质是三层循环,那么时间复杂度为,确实效率及其的低下。
class Solution:
# to check the string s is a palindrome or not
# return -1 : not
# return 1 : yes
def isPalindrome(self,s):
mid = int((len(s)+1)/2)
for x in range(0,mid):
if s[x] != s[len(s)-x-1]:
return -1
return 1
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
# special case
if len(s)<=1:
return s
# record the max length and max string
maxlength = 1
maxstring = s[0]
# use the brute-force approach to traverse all substring to find wheather it is a palindrome
for i in range(0,len(s)):
index = s.rfind(s[i])
if index > i:
for j in range(i+1,index+1):
if self.isPalindrome(s[i:j+1]) == 1:
if j-i+1 > maxlength:
maxlength = j-i+1
maxstring = s[i:j+1]
return maxstring
由于今天脑细胞瞎折腾太多了,dp的方法明日再细思一下,完成后再更新。
2019年4月8日
重新拾起Leetcode,开始每日刷题,本题补上两个AC的方法,分别是贪婪思想和动规思想,如下:
Version 2(Greedy):
遍历字符串的所有字符,以当前字符s[i] 为中心(分情况,奇数长度回文串中心为 s[i],偶数长度回文串中心 为 s[i] 和 s[i+1],且需要注意索引出界,和 s[i] 和 s[i+1]字符是否相同),同时向左右探索,是否有回文串,遇到第一个不满足的字符便退出(以当前字符s[i] 为中心的短字符串不是回文串,那么更长的也一定不会是回文串),此时要注意类似于木桶效应,以字符s[i] 为中心的字符串最长不超过最近的左或右字符串。
算法复杂度为
class Solution(object):
# use greedy to solve the problem
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if len(s) <= 1:
return s
MaxStr = ""
for i in range(len(s)):
str_odd = self.long_Palindrome_of_current_location(s, i, i)
str_even = self.long_Palindrome_of_current_location(s, i, i + 1)
if len(str_odd) > len(str_even):
if len(str_odd) > len(MaxStr):
MaxStr = str_odd
else:
if len(str_even) > len(MaxStr):
MaxStr = str_even
return MaxStr
def long_Palindrome_of_current_location(self, s, current1, current2):
# deal with out of index
if current2 >= len(s):
return ""
# deal with even situation to check if the center is palindrome
if s[current1] != s[current2]:
return ""
i = 1
while(i <= current1 and i <= len(s) - current2 - 1):
if s[current1 - i] != s[current2 + i]:
break
i += 1
return s[current1 - i + 1: current2 + i]
Version 3(DP):
使用动规的原因是每个回文字符串都可以结合小的字符串和边界字符进行判断,将我笨笨的推演和启发过程展示如下:
例子:'babad'
结果:'bab'(或 'aba')
首先dp_list应该为 5*5 的二维数组(或者矩阵亦可,看自己编程习惯与需要),全部初始化为 0,dp_list[i][j] = 0(或1)表示字符串s[j:i+1] 不是回文串(或是回文串),这个数组实际上我们只会填充了一半。原因是如dp_list[4][2] 代表了s[2:5] 是否是回文串的情况,但是我们不需要知道s[4:3] 是否是回文串的情况,所以dp_list[2][4] 没有意义,并未进行填充。
外循环 i 从 0 遍历到 len(s) - 1,内循环从 j = i 遍历到 0.
i = 0 时 j = 0 ,判断 s[0] == s[0],dp_list[0][0] = True;
i = 1 时 j = 1 ,判断 s[1] == s[1],dp_list[1][1] = True;
j = 0 ,判断 s[1] == s[0],dp_list[1][0] = False;
i = 2 时 j = 2 ,判断 s[i2] == s[2],dp_list[2][2] = True;
j = 1 ,判断 s[2] == s[1],dp_list[2][1] = False;
j = 0 ,判断 s[2] == s[0] 和 dp_list[1][1] = = True,dp_list[2][0] = True;
归纳可得,当i - j <= 1,只需判断s[i] == s[j]
当i - j >= 2,需要判断s[i] == s[j] 和 dp_list[i - 1][j + 1] = = True
动态规划方程如下
动态规划算法的时间复杂度是
class Solution2(object):
# use dp to solve the problem
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if len(s) <= 1:
return s
dp_list = [([0] * len(s)) for i in range(len(s))]
long_Palindrom = s[0]
start = 0
end = 1
for i in range(len(s)):
j = i
while(j >= 0):
if i - j <= 1:
dp_list[i][j] = (s[i] == s[j])
else:
dp_list[i][j] = (s[i] == s[j] and dp_list[i - 1][j + 1])
if (dp_list[i][j] == True ) and (i - j + 1 > end - start):
long_Palindrom = s[j : i + 1]
start = j
end = i + 1
j = j - 1
return long_Palindrom
三个版本的方法可以看出,第一个版本由于时间受限没有AC 通过,greedy 和 DP两个方法都通过了AC,但是它们时间上会有差异,可能会有这样一个疑问,为什么时间复杂度都是 ,但是贪婪方法比较快,由于外循环都是一样的遍历整个字符串,分析内循环即可知,贪婪是以当前字符为中心,每次增加两个字符的扩充字符串,所以本身内循环就只会进行N/2次,再加上遇到第一个不是回文串的时候就提前退出了,动态规划则没有提前退出的情况,所以可以看到两个方法运行时间上会有差别。
说来真的是太惭愧了,不合格的研二狗,阔别半年多才回归,接下来希望再接再厉,也不立flag了。有纰漏之处万望见谅,请联系博主。