Python 优化求解回文的时间/空间复杂性
Twitter最近提出了这个问题: 回文是一系列前后读相同内容的字符。给定一个字符串s,查找s中最长的回文子字符串 例如:Python 优化求解回文的时间/空间复杂性,python,performance,optimization,Python,Performance,Optimization,Twitter最近提出了这个问题: 回文是一系列前后读相同内容的字符。给定一个字符串s,查找s中最长的回文子字符串 例如: Input: "banana" Output: "anana" Input: "million" Output: "illi" 所以我就这样解决了 class Solution: def longestPalindrome(self, s): index = 0
Input: "banana"
Output: "anana"
Input: "million"
Output: "illi"
所以我就这样解决了
class Solution:
def longestPalindrome(self, s):
index = 0
longestPalindrome = ""
for x in s:
subStr = s[index + 1:]
nextIndex = subStr.find(x)
while nextIndex != -1:
txt = x + subStr
pland = txt[:nextIndex + 2]
if self.isPalindromicSubString(pland):
if len(pland) > len(longestPalindrome):
longestPalindrome = pland
nextIndex = subStr.find(x,nextIndex + 1)
index = index + 1
return longestPalindrome
def isPalindromicSubString(self,subStr):
index = 0
reverseIndex = -1
isItPalindromic = True
for y in subStr:
if y != subStr[reverseIndex]:
isItPalindromic = False
index = index + 1
reverseIndex = reverseIndex - 1
return isItPalindromic
# Test program
s = "abcdef aka"
print(str(Solution().longestPalindrome(s)))
# racecar
它工作正常,时间复杂度为O(N^2)
有没有办法做得更好,给我你对时间和空间复杂性的意见首先,我不知道你为什么要用clas来做这个。在python中,如果不使用OOP,我们不会编写类,而您不是 简介 我经过了将是回文的子字符串*1,
从中间向左移动,直到子字符串不再是回文
否则,字符串的末尾将被满足 代码
def find_longest_p(s):
pal_start=0;pal_end=1#最长的索引
对于范围(0,len(s)*2-1)内的半_指数:
#关于第1范围边界
#它经过回文的中间部分
#在某个字符或2个字符之间
#这个字符是s[half_index/2]
#其中,对于奇数,它是s[x.5],表示s[x]a和s[x+1]
c=b=-1#以值开始,因为for else要求b,c
对于范围内的b((半索引+半索引%2)//2-1,-1,-1):
#从左边的第一个索引开始,到0
#“a b c d”(str“abcd”,忽略空格)
#0123456(半指数)
#对于半指数==4
#b开始->1->0
#对于半指数==3
#b开始->1->0
c=半指数-b#b的对称指数
如果cpal\U end-pal\U start:
pal_start=b+1
pal_end=c
打破
其他:
#左边禁区
#算术有点变化
长度=c-b+1
如果长度>pal\U end-pal\U start:
pal_start=b
pal_end=c+1
返回s[pal\u开始:pal\u结束]
复杂性
这似乎有O(n^2)事实并非如此,因为在一个普通字符串中,回文不多。
这意味着第二个循环通常只有一次迭代
花费O(n)时间复杂性,
和额外的O(1)空间,定义了3个变量和一些中间产品,
或者O(n)空格如果包含复制和切片(这是不可避免的) 脚注 *1回文的中间可以在某个索引处,也可以在两个索引之间
因此,我通过了双重索引
中间值应该是:0,0.5,1,1.5,…
我先说:0,1,2,3,…我不知道你为什么要用clas来做这个。在python中,如果不使用OOP,我们不会编写类,而您不是 简介 我经过了将是回文的子字符串*1,
从中间向左移动,直到子字符串不再是回文
否则,字符串的末尾将被满足 代码
def find_longest_p(s):
pal_start=0;pal_end=1#最长的索引
对于范围(0,len(s)*2-1)内的半_指数:
#关于第1范围边界
#它经过回文的中间部分
#在某个字符或2个字符之间
#这个字符是s[half_index/2]
#其中,对于奇数,它是s[x.5],表示s[x]a和s[x+1]
c=b=-1#以值开始,因为for else要求b,c
对于范围内的b((半索引+半索引%2)//2-1,-1,-1):
#从左边的第一个索引开始,到0
#“a b c d”(str“abcd”,忽略空格)
#0123456(半指数)
#对于半指数==4
#b开始->1->0
#对于半指数==3
#b开始->1->0
c=半指数-b#b的对称指数
如果cpal\U end-pal\U start:
pal_start=b+1
pal_end=c
打破
其他:
#左边禁区
#算术有点变化
长度=c-b+1
如果长度>pal\U end-pal\U start:
pal_start=b
pal_end=c+1
返回s[pal\u开始:pal\u结束]
复杂性
这似乎有O(n^2)事实并非如此,因为在一个普通字符串中,回文不多。
这意味着第二个循环通常只有一次迭代
花费O(n)时间复杂性,
和额外的O(1)空间,定义了3个变量和一些中间产品,
或者O(n)空格如果包含复制和切片(这是不可避免的) 脚注 *1回文的中间可以在某个索引处,也可以在两个索引之间
因此,我通过了双重索引
中间值应该是:0,0.5,1,1.5,…
我去了:0,1,2,3,…这里是Manacher算法的python实现,来自Kevin链接的
def最长回文子串(s:str)->str:
#在s的字符之间和s的周围添加哨兵字符,以处理等长回文
#无需边界检查即可退出回文扩展循环的不同外字符
#以防整个字符串是回文
带|边界=“@|”+“|”。加入(s)+“|!”
#以新字符串中每个索引为中心的回文长度
回文长度=[0表示有边界的回文长度]
#当前回文中心
中心电流=0
#cu的右边界
class Solution:
def longestPalindrome(self, s):
index = 0
longestPalindrome = ""
for x in s:
subStr = s[index + 1:]
nextIndex = subStr.find(x)
while nextIndex != -1:
txt = x + subStr
pland = txt[:nextIndex + 2]
if self.isPalindromicSubString(pland):
if len(pland) > len(longestPalindrome):
longestPalindrome = pland
nextIndex = subStr.find(x,nextIndex + 1)
index = index + 1
return longestPalindrome
def isPalindromicSubString(self,subStr):
index = 0
reverseIndex = -1
isItPalindromic = True
for y in subStr:
if y != subStr[reverseIndex]:
isItPalindromic = False
index = index + 1
reverseIndex = reverseIndex - 1
return isItPalindromic
# Test program
s = "abcdef aka"
print(str(Solution().longestPalindrome(s)))
# racecar
# Question: https://stackoverflow.com/questions/62838784/optimize-time-space-complexity-for-solving-palindromes
import re
import time
def longestPalindrome(string, longest_first=True):
palindrome = None
order = range(len(string)//2, -1, -1) if longest_first else range(1, len(string)//2+1)
for n in order:
# Example: re.sub(r'^.*(\w)(\w)(\w)(\w)(\w)?\3\2\1.*$', r'\1\2\3\4\3\2\1', s)
regex_match = "".join([
r'^.*',
r'(\w)' * n,
r'(\w)?',
''.join([ f'\\{i}' for i in range(n,0,-1) ]),
r'.*$'
])
if re.match(regex_match, string):
regex_replace = "".join([ f'\\{i}' for i in list(range(1,n+2))+list(range(n,0,-1)) ])
palindrome = re.sub(regex_match, regex_replace, string)
if longest_first:
return palindrome # return the first match
else:
if not longest_first:
break # return the last match
return palindrome
if __name__ == '__main__':
for longest_first in [True, False]:
print(f'\nLongest First = {longest_first}')
for sentence in [
"banana",
"tracecars",
"detartrated",
"saippuakivikauppias",
"this is not the palindrome you are looking for"
]:
start_time = time.perf_counter()
answer = longestPalindrome(sentence, longest_first)
time_taken = time.perf_counter() - start_time
print(f'len({len(sentence):2d}) in {1000*time_taken:6.2f}ms = longestPalindrome({sentence}, {longest_first}) == {answer}')
assert longestPalindrome("banana") == "anana"
assert longestPalindrome("tracecars") == "racecar"
assert longestPalindrome("detartrated") == "detartrated"
Longest First = True
len( 6) in 0.79ms = longestPalindrome(banana, True) == anana
len( 9) in 0.39ms = longestPalindrome(tracecars, True) == racecar
len(11) in 0.41ms = longestPalindrome(detartrated, True) == detartrated
len(19) in 0.59ms = longestPalindrome(saippuakivikauppias, True) == saippuakivikauppias
len(46) in 13.19ms = longestPalindrome(this is not the palindrome you are looking for, True) == oo
Longest First = False
len( 6) in 0.06ms = longestPalindrome(banana, False) == anana
len( 9) in 0.08ms = longestPalindrome(tracecars, False) == racecar
len(11) in 0.19ms = longestPalindrome(detartrated, False) == detartrated
len(19) in 0.46ms = longestPalindrome(saippuakivikauppias, False) == saippuakivikauppias
len(46) in 0.04ms = longestPalindrome(this is not the palindrome you are looking for, False) == oo