[英]Why won't my for loop work? (Python)
是的,这是家庭作业。 我只是想了解为什么这似乎不起作用。
我正在尝试按字母顺序查找字符串中最长的子字符串。 我列出了一个随机字母列表,并说长度为19。当我运行代码时,它会打印出索引0到17。(我知道发生这种情况是因为我从范围中减去1)但是,当我将其省略时- 1,它告诉我“字符串索引超出范围”。 为什么会这样呢?
s = 'cntniymrmbhfinjttbiuqhib'
sub = ''
longest = []
for i in range(len(s) - 1):
if s[i] <= s[i+1]:
sub += s[i]
longest.append(sub)
elif s[i-1] <= s[i]:
sub += s[i]
longest.append(sub)
sub = ' '
else:
sub = ' '
print(longest)
print ('Longest substring in alphabetical order is: ' + max(longest, key=len))
我也尝试了其他方法
如果我只是说:
for i in s:
它将引发错误,并说“字符串索引必须是整数,而不是str。” 这似乎是一种遍历字符串的简单得多的方法,但是我将如何以这种方式比较各个字母呢?
顺便说一下,这是Python 2.7。
编辑:我确定我的if / elif语句可以改进,但这是我想到的第一件事。 如有需要,我可以稍后再谈。
问题是if s[i] <= s[i+1]:
。 如果i=18
(循环的最后一次迭代不包含-1
)。 然后i+1=19
超出范围。
请注意, elif s[i-1] <= s[i]:
也可能未按照您希望的方式执行。 当i=0
我们有i-1 = -1
。 Python允许负索引表示从索引对象的后面开始计数,因此s[-1]
是列表中的最后一个字符(s [-2]将是倒数第二个,依此类推)。
获取上一个和下一个字符的一种更简单的方法是使用zip
同时将字符串切成分别从第一个和第二个字符开始计数。
如果您以前从未看过zip
工作原理如下:
>>> for char, x in zip(['a','b','c'], [1,2,3,4]):
>>> print char, x
'a' 1
'b' 2
'c' 3
因此,您可以执行以下操作:
for previous_char, char, next_char in zip(string, string[1:], string[2:]):
遍历所有三元组字符而不会弄乱结尾。
但是,有一种更简单的方法可以执行此操作。 不应将字符串中的当前字符与字符串中的其他字符进行比较,而应将其与字母字符中当前字符串的最后一个字符进行比较,例如:
s = "abcdabcdefa"
longest = [s[0]]
current = [s[0]]
for char in s[1:]:
if char >= current[-1]: # current[-1] == current[len(current)-1]
current.append(char)
else:
current=[char]
if len(longest) < len(current):
longest = current
print longest
这样可以避免进行任何华丽的索引编制。
我确定我的if / elif语句可以改进,但这是我首先想到的。 如有需要,我可以稍后再谈。
@ or1426的解决方案创建当前最长排序序列的列表,并在找到更长序列时将其复制到longest
序列。 每当找到更长的序列时,都会创建一个新列表,并将其添加到每个字符的列表中。 在Python中,这实际上非常快,但是请参见下文。
@Deej的解决方案将当前最长的序列保留在字符串变量中,并且每次找到更长的子字符串(即使它是当前序列的延续)时,该子字符串也会保存到列表中。 该列表最终具有原始字符串的所有已排序子字符串,并且最长的字符串是通过调用max
来找到的。
这是一个更快的解决方案,它仅跟踪当前最大序列的索引,并且仅当找到不按顺序排列的字符时才将更改最长。
def bjorn4(s):
# we start out with s[0] being the longest sorted substring (LSS)
longest = (0, 1) # the slice-indices of the longest sorted substring
longlen = 1 # the length of longest
cur_start = 0 # the slice-indices of the *current* LSS
cur_stop = 1
for ch in s[1:]: # skip the first ch since we handled it above
end = cur_stop-1 # cur_stop is a slice index, subtract one to get the last ch in the LSS
if ch >= s[end]: # if ch >= then we're still in sorted order..
cur_stop += 1 # just extend the current LSS by one
else:
# we found a ch that is not in sorted order
if longlen < (cur_stop-cur_start):
# if the current LSS is longer than longest, then..
longest = (cur_start, cur_stop) # store current in longest
longlen = longest[1] - longest[0] # precompute longlen
# since we can't add ch to the current LSS we must create a new current around ch
cur_start, cur_stop = cur_stop, cur_stop+1
# if the LSS is at the end, then we'll not enter the else part above, so
# check for it after the for loop
if longlen < (cur_stop - cur_start):
longest = (cur_start, cur_stop)
return s[longest[0]:longest[1]]
快多少? 它的速度几乎是orl1426的两倍,是deej的三倍。 与往常一样,这取决于您的输入。 存在的排序子字符串块越多,上述算法与其他算法相比将越快。 例如,在长度为100000的输入字符串中,包含交替的100个随机字符和100个有序字符,我得到:
bjorn4: 2.4350001812
or1426: 3.84699988365
deej : 7.13800001144
如果我将其更改为交替使用1000个随机字符和1000个排序的字符,则得到:
bjorn4: 23.129999876
or1426: 38.8380000591
deej : MemoryError
更新:这是我的算法的进一步优化版本,带有比较代码:
import random, string
from itertools import izip_longest
import timeit
def _randstr(n):
ls = []
for i in range(n):
ls.append(random.choice(string.lowercase))
return ''.join(ls)
def _sortstr(n):
return ''.join(sorted(_randstr(n)))
def badstr(nish):
res = ""
for i in range(nish):
res += _sortstr(i)
if len(res) >= nish:
break
return res
def achampion(s):
start = end = longest = 0
best = ""
for c1, c2 in izip_longest(s, s[1:]):
end += 1
if c2 and c1 <= c2:
continue
if (end-start) > longest:
longest = end - start
best = s[start:end]
start = end
return best
def bjorn(s):
cur_start = 0
cur_stop = 1
long_start = cur_start
long_end = cur_stop
for ch in s[1:]:
if ch < s[cur_stop-1]:
if (long_end-long_start) < (cur_stop-cur_start):
long_start = cur_start
long_end = cur_stop
cur_start = cur_stop
cur_stop += 1
if (long_end-long_start) < (cur_stop-cur_start):
return s[cur_start:cur_stop]
return s[long_start:long_end]
def or1426(s):
longest = [s[0]]
current = [s[0]]
for char in s[1:]:
if char >= current[-1]: # current[-1] == current[len(current)-1]
current.append(char)
else:
current=[char]
if len(longest) < len(current):
longest = current
return ''.join(longest)
if __name__ == "__main__":
print 'achampion:', round(min(timeit.Timer(
"achampion(rstr)",
setup="gc.enable();from __main__ import achampion, badstr; rstr=badstr(30000)"
).repeat(15, 50)), 3)
print 'bjorn:', round(min(timeit.Timer(
"bjorn(rstr)",
setup="gc.enable();from __main__ import bjorn, badstr; rstr=badstr(30000)"
).repeat(15, 50)), 3)
print 'or1426:', round(min(timeit.Timer(
"or1426(rstr)",
setup="gc.enable();from __main__ import or1426, badstr; rstr=badstr(30000)"
).repeat(15, 50)), 3)
输出:
achampion: 0.274
bjorn: 0.253
or1426: 0.486
将数据更改为随机数据:
achampion: 0.350
bjorn: 0.337
or1426: 0.565
并排序:
achampion: 0.262
bjorn: 0.245
or1426: 0.503
“不,不,它还没死,它正在休息”
现在,Deej有了答案,我对发布作业的答案感到更加自在。
只需对@Deej的逻辑重新排序,您可以简化为:
sub = ''
longest = []
for i in range(len(s)-1): # -1 simplifies the if condition
sub += s[i]
if s[i] <= s[i+1]:
continue # Keep adding to sub until condition fails
longest.append(sub) # Only add to longest when condition fails
sub = ''
max(longest, key=len)
但是正如@thebjorn所提到的那样,这具有将每个升序分区保留在列表中(在内存中)的问题。 您可以使用生成器来解决此问题,我仅将其余内容用于指导目的:
def alpha_partition(s):
sub = ''
for i in range(len(s)-1):
sub += s[i]
if s[i] <= s[i+1]:
continue
yield sub
sub = ''
max(alpha_partition(s), key=len)
当然,这不是最快的解决方案(字符串构造和索引),但是更改非常简单,请使用zip避免在字符串中建立索引,并使用索引避免字符串构造和添加:
from itertools import izip_longest # For py3.X use zip_longest
def alpha_partition(s):
start = end = 0
for c1, c2 in izip_longest(s, s[1:]):
end += 1
if c2 and c1 <= c2:
continue
yield s[start:end]
start = end
max(alpha_partition(s), key=len)
由于生成器开销,它应该运行得非常有效,并且比@thebjorn的迭代索引方法稍慢一些。
使用s * 100
alpha_partition()
:1000个循环,每个循环最好3:448 µs
@thebjorn:1000个循环,最好为3:每个循环389 µs
作为参考,将生成器转换为迭代函数:
from itertools import izip_longest # For py3.X use zip_longest
def best_alpha_partition(s):
start = end = longest = 0
best = ""
for c1, c2 in izip_longest(s, s[1:]):
end += 1
if c2 and c1 <= c2:
continue
if (end-start) > longest:
longest = end - start
best = s[start:end]
start = end
return best
best_alpha_partition(s)
best_alpha_partition()
:1000个循环,最好为3:每个循环306 µs
我个人更喜欢生成器形式,因为您将使用完全相同的生成器来查找最小值,前5个,等等。它非常可重用,而迭代函数只做一件事。
好的,因此在阅读了您的回答并尝试了各种不同的方法之后,我终于想出了一种完全可以满足我需求的解决方案。 它不是最漂亮的代码,但是可以工作。 我确定提到的解决方案也可以使用,但是我无法弄清楚。 这是我所做的:
s = 'inaciaebganawfiaefc'
sub = ''
longest = []
for i in range(len(s)):
if (i+1) < len(s) and s[i] <= s[i+1]:
sub += s[i]
longest.append(sub)
elif i >= 0 and s[i-1] <= s[i]:
sub += s[i]
longest.append(sub)
sub = ''
else:
sub = ''
print ('Longest substring in alphabetical order is: ' + max(longest, key=len))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.