import bisect
def lis(xs):
ret = []
for x in xs:
i = bisect.bisect_left(ret, x)
ret[i:i+1] = x,
return len(ret)
Example:
>>> lis([10, 1, 2, 3])
3
>>> lis([10,22,9,33,21,50,41,60,80])
6
NOTE In the above code,ret
does not contains valid subsequence. But the length is correct. Use the above code what you want is only the length of the LIS.
Explanation:
Think about [10,22,9,11]
. There could be two LIS: [10,22]
, [9,11]
. The ret
in the above lis
function maintains the following condition:
ret
is sorted; can use binary search (bisect.bisect_left)
ret[i]
contains the minimum last value for length-i
subsequence.
ret
changes as... (given [10,22,9,11]
as input)
- [10] - after processing the 1st element
- [10, 22] - after processing the 2nd element
- [9, 22] - ... Now the minimum value for 1-length subsequence is 9! 10 is overwritten.
- [9, 11]
Time complexity: O(nk) where n is the number of list item, k is the length of the LIS.
UPDATE
Modified version that correctly get the subsequence:
import bisect
def lis(xs):
if not xs: return []
prev = {}
ret = []
for x in xs:
i = bisect.bisect_left(ret, x)
ret[i:i+1] = x,
prev[x] = ret[i-1] if i > 0 else None
subseq = [ret[-1]]
while subseq[-1] is not None:
subseq.append(prev[subseq[-1]])
return list(reversed(subseq[:-1]))
Example:
>>> lis([])
[]
>>> lis([10, 1, 2, 3])
[1, 2, 3]
>>> lis([10,22,9,33,21,50,41,60,80])
[10, 22, 33, 41, 60, 80]
>>> lis([1,5,2,6,3,4])
[1, 2, 3, 4]
>>> lis([100,200,1,2,3])
[1, 2, 3]