我试图解决LeetCode上的Longest Palindromic Substring问题。问题陈述是:
给定一个字符串s,找到s中最长的回文子字符串。您可以假设s的最大长度为1000。
示例:
Input: "babad"
Output: "bab"
注意:" aba"也是一个有效的答案。 例如:
Input: "cbbd"
Output: "bb"
我提出了以下解决方案(包括一些测试用例):
import pytest
class Solution:
def longestPalindrome(self, s):
candidate = ""
longest = ""
contains_palindrome = False
for i, char in enumerate(s):
if i == 0:
candidate = char
elif i == 1:
if s[1] == s[0]:
candidate = self.get_palindrome(s, start=0, end=1)
elif i >= 2:
if char == s[i-1]:
candidate = self.get_palindrome(s, start=i-1, end=i)
elif char == s[i-2]:
candidate = self.get_palindrome(s, start=i-2, end=i)
if len(candidate) > len(longest):
longest = candidate
return longest
@staticmethod
def get_palindrome(s, start, end):
palindrome = s[start:end+1]
while end < len(s) - 1:
if s[end+1] == s[start] and Solution.all_same(palindrome):
end += 1
palindrome += s[end]
else:
break
while (start > 0) and (end < len(s) - 1):
start -= 1
end += 1
if s[start] == s[end]:
palindrome = s[start] + palindrome + s[end]
else:
break
return palindrome
@staticmethod
def all_same(items):
return all(item == items[0] for item in items)
def test_1():
assert Solution().longestPalindrome("babad") == "bab"
def test_2():
assert Solution().longestPalindrome("cbbd") == "bb"
def test_3():
assert Solution().longestPalindrome("abba") == "abba"
def test_4():
assert Solution().longestPalindrome("a") == "a"
def test_5():
assert Solution().longestPalindrome("ccc") == "ccc"
def test_6():
assert Solution().longestPalindrome("aaaa") == "aaaa"
def test_7():
assert Solution().longestPalindrome("aaabaaaa") == "aaabaaa"
if __name__ == "__main__":
pytest.main([__file__])
问题是我超过了时间限制&#34;错误:
我的理解是这个算法的时间复杂度是O(n ^ 2),因为对于每个字符,它检查一个最多可达n个字符的回文。在LeetCode的解决方案中,还有O(n ^ 2)算法(在Java中)。
我猜测Python的时间限制有点过于严格,这比Java要慢。或者我错过了什么,我的解决方案的时间复杂度实际上大于O(n ^ 2)?
答案 0 :(得分:2)
您失败的测试字符串似乎只包含一个。这是最坏的情况,它实际上是O(n³)而不是O(n²),因为all_same
中有另一个隐藏的循环。 (起初,我还认为字符串上的切片运算符[:]
会复制,但事实并非如此。)
您需要致电all_same
,因为您在主要功能中区分了“aa”和“aba”的情况。但是您不需要在循环中执行此操作,因为您将在while
的第一个get_palindrome
循环中仅添加相同的字母。因此,快速解决方法是测试所有字符是否只相同一次:
if Solution.all_same(palindrome):
while end < len(s) - 1:
if s[end+1] == s[start]:
end += 1
palindrome += s[end]
else:
break
现在all_same
os运行在两个或三个字母的字符串上并且速度很快。
更好的解决方案根本不需要all_same
。当你可以传递“b”并让该函数执行其余工作时,为什么将“aba”传递给get_palindrome
:
elif i >= 2:
if char == s[i-1]:
candidate = self.get_palindrome(s, start=i-1, end=i)
else:
candidate = self.get_palindrome(s, start=i, end=i)
总体而言,代码看起来相当凌乱,包含所有break
和不必要的大小写区别。为什么将索引和palindrome
保留为get_palindrome
中的单独实体,您必须保持同步?
在我看来,这是一个更整洁的版本:
class Solution:
def longestPalindrome(self, s):
longest = ""
for i, _ in enumerate(s):
candidate = self.get_palindrome(s, start = i, end = i)
if len(candidate) > len(longest):
longest = candidate
return longest
@staticmethod
def get_palindrome(s, start, end):
while end + 1 < len(s) and s[end+1] == s[start]:
end += 1
while start > 0 and end + 1 < len(s) and s[start - 1] == s[end + 1]:
start -= 1
end += 1
return s[start:end + 1]
即便如此,还有改进的余地:对于字符串“aaaa”,代码仍然会考虑“aaaa”,“aaa”,“aa”和“a”。 while
中的第一个get_palindrome
会一路走来,但却没有机会找到更好的打击。我们可以通过在主函数中找到相同字母的延伸来改善这一点:
class Solution:
def longestPalindrome(self, s):
longest = ""
i = 0
l = len(s)
while i < l:
end = i
while end + 1 < l and s[end + 1] == s[i]:
end += 1
candidate = self.get_palindrome(s, i, end)
if len(candidate) > len(longest):
longest = candidate
i = end + 1
return longest
@staticmethod
def get_palindrome(s, start, end):
while start > 0 and end + 1 < len(s) and s[start - 1] == s[end + 1]:
start -= 1
end += 1
return s[start:end + 1]
对于像“abababab”这样的字符串,这仍然不太理想,但在你的情况下应该足够快。
答案 1 :(得分:1)
我尝试了“动态编程”的想法,找到“0阶”回文中心然后修剪,因为深度j
增加并且出现不匹配
修剪是在列表comps内完成的,应该相对较快,但仍然是O(n ^ 2)
class Solution:
def longestPalindrome(self, s):
s = '>' + s + '<' # add guard values
# make lists of '0_th order' palindrome 'centers', even and odd
evn = [i for i, a in enumerate(zip(s, s[1:])) if a[0] == a[1]]
odd = [i + 1 for i, a in enumerate(zip(s, s[2:])) if a[0] == a[1]]
# prune lists of centers when elements +/- j from centers don't match
evn_last, odd_last = [[1], 0], [[1], 1]
j = 1
while evn:
evn_last = (evn, j)
evn = [e for e in evn if s[e - j] == s[e + j + 1]]
j += 1
j = 1
while odd:
odd_last = (odd, j)
odd = [e for e in odd if s[e - j] == s[e + j]]
j += 1
# determine longest, construct palindrome
if 2 * evn_last[1] > 2 * odd_last[1] - 1:
cntr = evn_last[0][0]
pal = s[cntr] + s[cntr + 1]
for i in range(1, evn_last[1]):
pal = s[cntr - i] + pal + s[cntr + i + 1]
else:
cntr = odd_last[0][0]
pal = s[cntr]
for i in range(1, odd_last[1]):
pal = s[cntr - i] + pal + s[cntr + i]
return pal
如果我错误地粘贴到类包装器中,我会道歉 - OOP不是我的事
确实通过了你的测试
可能已经找到了调用实例,明显重命名
S = Solution()
%timeit S.fred_longestPalindrome("aba"*300)
17.8 ms ± 230 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit S.Kurt_longestPalindrome("aba"*300)
52.8 ms ± 108 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
答案 2 :(得分:0)
这是我发现最长回文及其长度的方法。我认为这很容易理解。
首先,我将单词添加到char数组中,然后我将第一个字母与所有其他字母一起向后检查。然后像这样移动到下一个字符。使用if else和for循环获取答案,最后使用哈希集,我得到了最长的回文。
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println(longestPalSubstr(in.nextLine().toLowerCase()));
}
static String longestPalSubstr(String str) {
char [] input = str.toCharArray();
Set<CharSequence> out = new HashSet<CharSequence>();
int n1 = str.length()-1;
for(int a=0;a<=n1;a++)
{
for(int m=n1;m>a;m--)
{
if(input[a]==input[m])
{
String nw = "",nw2="";
for (int y=a;y<=m;y++)
{
nw=nw+input[y];
}
for (int t=m;t>=a;t--)
{
nw2=nw2+input[t];
}
if(nw2.equals(nw))
{
out.add(nw);
break;
}
}
}
}
int a = out.size();
int maxpos=0;
int max=0;
Object [] s = out.toArray();
for(int q=0;q<a;q++)
{
if(max<s[q].toString().length())
{
max=s[q].toString().length();
maxpos=q;
}
}
String output = "longest palindrome is : "+s[maxpos].toString()+" and the lengths is : "+ max;
return output;
}
此方法将返回最大长度回文及其长度。这是我尝试并获得答案的一种方式。并且此方法无论其是奇数长度还是偶数长度都将运行。
答案 3 :(得分:0)
class Solution:
def longestPalindrome(self, s):
paliandr = ''
len_s = len(s)
def if_pal_singl(s,i,dabl):
pal = s[i-dabl:i+1]
indx_left = i-dabl
indx_right = i
while (indx_left-1 in range(len_s) and indx_right+1 in range(len_s)):
indx_left -=1
indx_right +=1
if s[indx_left] == s[indx_right]:
pal = s[indx_left]+pal+s[indx_right]
else:
break
return pal
dabl = 0
for i in range(1,len_s-1):
if s[i] == s[i+1]:
dabl+=1
continue
pal = if_pal_singl(s,i,dabl)
dabl = 0
if len(pal) > len(paliandr):
paliandr = pal
print (paliandr)
if __name__ == "__main__":
Solution().longestPalindrome('abababab')