Python 和小于或等于k的最长子数组的长度
在一次采访中,我被问到这样一个问题:给定一些正整数数组s,求最长子数组的长度,使其所有值之和小于或等于某个正整数k。每个输入将始终至少有一个解决方案。数组不是圆形的 我开始编写一个动态规划解决方案,通过在从0到k的越来越大的值中找到最大长度来工作 这是我用python编写的代码,其中有一个错误我似乎找不到,我的答案总是相差几位数:Python 和小于或等于k的最长子数组的长度,python,algorithm,dynamic-programming,Python,Algorithm,Dynamic Programming,在一次采访中,我被问到这样一个问题:给定一些正整数数组s,求最长子数组的长度,使其所有值之和小于或等于某个正整数k。每个输入将始终至少有一个解决方案。数组不是圆形的 我开始编写一个动态规划解决方案,通过在从0到k的越来越大的值中找到最大长度来工作 这是我用python编写的代码,其中有一个错误我似乎找不到,我的答案总是相差几位数: def maxLength(s, k): lengths = [0 for x in range(k)] for i in range(1,k+1):
def maxLength(s, k):
lengths = [0 for x in range(k)]
for i in range(1,k+1):
for j in range(len(s)):
if s[j] <= i and lengths[i - s[j]] + 1 > lengths[i]:
lengths[i] = lengths[i - s[j]] + 1
if i + 1 == len(s):
break
return lengths[-1]
def最大长度(s,k):
长度=[0表示范围(k)内的x]
对于范围(1,k+1)内的i:
对于范围内的j(len(s)):
如果s[j]长度[i]:
长度[i]=长度[i-s[j]]+1
如果i+1==len(s):
打破
返回长度[-1]
输入1:s=[1,2,3],k=4
输出1:2
输入2:s=[3,1,2,1],k=4
输出2:3
我认为这是可行的。。。(递归地从问题中取出连续的需求,因为这似乎与问题中提供的样本输出不匹配),OP提到问题是:
给定某个正整数数组s,求最长子数组的长度,使所有值之和等于某个正整数k
产出:
3
# BorrajaX's note: 2 + 1 + 3
2
# BorrajaX's note: 3 + 1
3
# BorrajaX's note: 1 + 2 + 1
3
# BorrajaX's note: 1 + 2 + 7
0
# BorrajaX's note: No possible sum
6
# BorrajaX's note: 1 + 1 + 1 + 1 + 1 + 1
编辑01:
如果您想获取给您的总和最长的列表,您可以这样做:
import copy
def longest_sum(input_list, used_list, target_number):
if target_number == 0:
return used_list
if not input_list:
return []
# Taken
used_list_taken = copy.copy(used_list)
used_list_taken.append(input_list[0])
used_1 = longest_sum(input_list[1:], used_list_taken, target_number - input_list[0])
# Not taken
used_list_not_taken = copy.copy(used_list)
used_2 = longest_sum(input_list[1:], used_list_not_taken, target_number)
if len(used_1) > len(used_2):
return used_1
else:
return used_2
if __name__ == "__main__":
print(longest_sum([2, 1, 8, 3, 4], [], 6))
print(longest_sum([1, 2, 3], [], 4))
print(longest_sum([3, 1, 2, 1], [], 4))
print(longest_sum([1, 2, 7, 8, 11, 12, 14, 15], [], 10))
print(longest_sum([1, 2, 3], [], 999))
print(longest_sum([1, 1, 1, 1, 1, 1, 4], [], 6))
def get_longes(a_list, k):
longest = 0
length = len(a_list)
for i in xrange(length):
s = 0
for j in xrange(i,length):
s+=a_list[j]
if s < k:
pass
elif s==k:
longest = j+1-i
else:
break
return longest
你会看到:
[2, 1, 3]
[1, 3]
[1, 2, 1]
[1, 2, 7]
[]
[1, 1, 1, 1, 1, 1]
ps1:如果没有递归提供的快速回溯功能,我真的不知道如何做到这一点。。。抱歉:-(
PS 2:如果这不是您想要的(我提到我从需求中删除了连续需求),请告诉我,我将删除此答案。稍微短一点,并假设为非圆形s:在s上滑动大小减小的窗口
def maxlen(s, k):
for win in range(k, 0, -1):
for i in range(0, len(s) - win):
if sum(s[i:i+win]) == k:
return win
return None
s = [3,1,2,3]
k = 4
print(maxlen(s, k))
可以在线性(O(n))时间内执行此操作:
这段代码可能看起来像是O(n),如果它是用Go编写的,那么它就是。请参见current=current[1:][/code>?根据,从列表中获取一个片段需要O(n)
我找不到从开始删除元素的列表操作,直到我突然意识到我不必这样做。current
始终是s
的一个连续子数组,那么为什么不只标记它的开始和结束呢
这是我的最终解决方案:
def max_length(s, k):
# These two mark the start and end of the subarray that `current` used to be.
subarray_start = 0
subarray_end = 0
subarray_sum = 0
max_len = -1 # returns -1 if there is no subsequence that adds up to k.
for i in s:
subarray_sum += i
subarray_end += 1
while subarray_sum > k: # Shrink the array from the left, until the sum is <= k.
subarray_sum -= s[subarray_start]
subarray_start += 1
if subarray_sum == k:
max_len = max(max_len, subarray_end - subarray_start)
return max_len
def最大长度(s,k):
#这两个标记了“current”以前的子数组的开始和结束。
子阵列_开始=0
子数组_end=0
子数组_和=0
max_len=-1#如果没有累加到k的子序列,则返回-1。
对于s中的i:
子阵_和+=i
子阵列_end+=1
当子数组_sum>k:#从左侧收缩数组,直到和为k:#从左侧收缩数组,直到和为原始答案
最初的问题是找到最长的子数组的长度,该子数组的总和为k
您可以遍历列表索引,将每个索引作为求和窗口的起点。然后将索引从起点索引遍历到终点,以标记窗口的终点。在每个步骤中,您都可以获取和,甚至更好,将其添加到一个和项中。如果和超过目标,您将打破内部循环,继续前进。
它将如下所示:
import copy
def longest_sum(input_list, used_list, target_number):
if target_number == 0:
return used_list
if not input_list:
return []
# Taken
used_list_taken = copy.copy(used_list)
used_list_taken.append(input_list[0])
used_1 = longest_sum(input_list[1:], used_list_taken, target_number - input_list[0])
# Not taken
used_list_not_taken = copy.copy(used_list)
used_2 = longest_sum(input_list[1:], used_list_not_taken, target_number)
if len(used_1) > len(used_2):
return used_1
else:
return used_2
if __name__ == "__main__":
print(longest_sum([2, 1, 8, 3, 4], [], 6))
print(longest_sum([1, 2, 3], [], 4))
print(longest_sum([3, 1, 2, 1], [], 4))
print(longest_sum([1, 2, 7, 8, 11, 12, 14, 15], [], 10))
print(longest_sum([1, 2, 3], [], 999))
print(longest_sum([1, 1, 1, 1, 1, 1, 4], [], 6))
def get_longes(a_list, k):
longest = 0
length = len(a_list)
for i in xrange(length):
s = 0
for j in xrange(i,length):
s+=a_list[j]
if s < k:
pass
elif s==k:
longest = j+1-i
else:
break
return longest
def-get-longes(列表,k):
最长=0
长度=长度(a_列表)
对于X范围内的i(长度):
s=0
对于X范围内的j(i,长度):
s+=a_列表[j]
如果s
这可以进一步加快速度,因为在外循环中移动一步时,不需要重置窗口大小。事实上,只要跟踪窗口大小,并在外循环继续移动时将其减小1。这样,您甚至可以摆脱内循环,在O(n)中写入内容:
def get_longest(a_list,k):
length=len(a_list)
l_length = 0
longest = 0
s = 0
for i in xrange(length):
while s<k: # while the sum is smaller, we increase the window size
if i+l_length==length: # this could also be handled with a try, except IndexError on the s+=a_list[... line
return longest
s+=a_list[i+l_length]
l_length+=1
if s == k: # if the sum is right, keep its length if it is bigger than the last match.
longest = max(l_length, longest)
l_length-=1 # keep the window end at the same position (as we will move forward one step)
s-=a_list[i] # and subtract the element that will leave the window
return longest
def get_longest(a_列表,k):
长度=长度(a_列表)
l_长度=0
最长=0
s=0
对于X范围内的i(长度):
虽然s这里有一个解决方案适用于任何可迭代的s
(甚至是迭代器)。它本质上与相同的算法,但如果k
相对于s
中的值较大(因此相关子序列的长度较长),则效率会更高:
因为我们在迭代时不保留包含我们正在检查的子序列的列表,所以这种基于迭代器的方法只有在您只需要最长子序列的长度,而不需要其实际内容时才有用
如果您想获得最长子序列的副本,可以在bigblind的答案上使用不同的变体,使用collections.dequeue
而不是列表(这样从左侧弹出很快),并像我的代码一样跟踪运行的总和(这样您就不需要反复调用sum
):
如果您的问题标题具有误导性,并且您实际上并不关心子序列是否连续,那么我认为您当前的动态规划方法可以满足您的要求。我只是不完全确定我是否理解循环的工作原理。我认为最自然的做法是在输入项上使用外循环,在p上使用第二个循环包含该值的势和(是长度
列表中的索引)。我还建议使用无
作为除0
以外的所有长度的初始值,因为如果没有特殊情况,很难使条件正常工作
def max_length(s, k):
lengths = [None for _ in range(k+1)]
lengths[0] = 0
for x in s:
for i in range(k, x-1, -1): # count down to avoid duplication
if lengths[i-x] is not None and (lengths[i] is None or
lengths[i-x] >= lengths[i]):
lengths[i] = lengths[i-x] + 1
return lengths[k]
这篇文章对你很有帮助
可以使用求和数组+二进制搜索来解决此问题。
你得到的第一个观察结果是如果我们
def get_longes(a_list, k):
longest = 0
length = len(a_list)
for i in xrange(length):
s = 0
for j in xrange(i,length):
s+=a_list[j]
if s < k:
pass
elif s==k:
longest = j+1-i
else:
break
return longest
def get_longest(a_list,k):
length=len(a_list)
l_length = 0
longest = 0
s = 0
for i in xrange(length):
while s<k: # while the sum is smaller, we increase the window size
if i+l_length==length: # this could also be handled with a try, except IndexError on the s+=a_list[... line
return longest
s+=a_list[i+l_length]
l_length+=1
if s == k: # if the sum is right, keep its length if it is bigger than the last match.
longest = max(l_length, longest)
l_length-=1 # keep the window end at the same position (as we will move forward one step)
s-=a_list[i] # and subtract the element that will leave the window
return longest
def get_longest_smaller_or_equal(a_list,k):
length=len(a_list)
l_length = 0
longest = 0
s = 0
for i in xrange(length):
while s<=k: # while the sum is smaller, we increase the window size
longest = max(l_length, longest)
if i+l_length==length: # this could also be handled with a try, except IndexError on the s+=a_list[... line
return longest
s+=a_list[i+l_length]
l_length+=1
l_length-=1 # keep the window end at the same position (as we will move forward one step)
s-=a_list[i] # and subtract the element that will leave the window
return longest
import itertools
def max_length(s, k):
head, tail = itertools.tee(s)
current_length = current_sum = 0
max_len = -1 # returns -1 if there is no subsequence that adds up to k.
for i in head:
current_length += 1
current_sum += i
while current_sum > k:
current_length -= 1
current_sum -= next(tail)
if current_sum == k:
max_len = max(max_len, current_sum)
return max_len
import collections
def max_subsequence(s, k):
current = collections.dequeue()
current_sum = 0
max_len = -1
max_seq = None # returns None if there is no valid subsequence.
for i in s:
current.append(i)
current_sum += i
while current_sum > k: # Shrink from the left efficiently!
current_sum -= current.popleft()
if current_sum == k:
if len(current) > max_len:
max_len = len_current
max_seq = list(current) # save a copy of the subsequence
return max_seq
def max_length(s, k):
lengths = [None for _ in range(k+1)]
lengths[0] = 0
for x in s:
for i in range(k, x-1, -1): # count down to avoid duplication
if lengths[i-x] is not None and (lengths[i] is None or
lengths[i-x] >= lengths[i]):
lengths[i] = lengths[i-x] + 1
return lengths[k]
sum[i] = sum[i−1] + array[i]
sum[i]=array[i]
sum[j]−sum[i], j>i