3 回答
TA贡献1719条经验 获得超6个赞
这是一个相当通用的解决方案:
O(n log n)及时运行
处理增加,减少,减少和增加的子序列,
与任何序列对象,包括工程list,numpy.array,str多,
通过key参数(类似于内置sorted函数中的参数)支持对象列表和自定义比较方法,
可以返回子序列的元素或其索引。
编码:
from bisect import bisect_left, bisect_right
from functools import cmp_to_key
def longest_subsequence(seq, mode='strictly', order='increasing',
key=None, index=False):
bisect = bisect_left if mode.startswith('strict') else bisect_right
# compute keys for comparison just once
rank = seq if key is None else map(key, seq)
if order == 'decreasing':
rank = map(cmp_to_key(lambda x,y: 1 if x<y else 0 if x==y else -1), rank)
rank = list(rank)
if not rank: return []
lastoflength = [0] # end position of subsequence with given length
predecessor = [None] # penultimate element of l.i.s. ending at given position
for i in range(1, len(seq)):
# seq[i] can extend a subsequence that ends with a lesser (or equal) element
j = bisect([rank[k] for k in lastoflength], rank[i])
# update existing subsequence of length j or extend the longest
try: lastoflength[j] = i
except: lastoflength.append(i)
# remember element before seq[i] in the subsequence
predecessor.append(lastoflength[j-1] if j > 0 else None)
# trace indices [p^n(i), ..., p(p(i)), p(i), i], where n=len(lastoflength)-1
def trace(i):
if i is not None:
yield from trace(predecessor[i])
yield i
indices = trace(lastoflength[-1])
return list(indices) if index else [seq[i] for i in indices]
我为上面没有粘贴的函数编写了一个文档字符串,以展示代码:
"""
Return the longest increasing subsequence of `seq`.
Parameters
----------
seq : sequence object
Can be any sequence, like `str`, `list`, `numpy.array`.
mode : {'strict', 'strictly', 'weak', 'weakly'}, optional
If set to 'strict', the subsequence will contain unique elements.
Using 'weak' an element can be repeated many times.
Modes ending in -ly serve as a convenience to use with `order` parameter,
because `longest_sequence(seq, 'weakly', 'increasing')` reads better.
The default is 'strict'.
order : {'increasing', 'decreasing'}, optional
By default return the longest increasing subsequence, but it is possible
to return the longest decreasing sequence as well.
key : function, optional
Specifies a function of one argument that is used to extract a comparison
key from each list element (e.g., `str.lower`, `lambda x: x[0]`).
The default value is `None` (compare the elements directly).
index : bool, optional
If set to `True`, return the indices of the subsequence, otherwise return
the elements. Default is `False`.
Returns
-------
elements : list, optional
A `list` of elements of the longest subsequence.
Returned by default and when `index` is set to `False`.
indices : list, optional
A `list` of indices pointing to elements in the longest subsequence.
Returned when `index` is set to `True`.
"""
一些例子:
>>> seq = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]
>>> longest_subsequence(seq)
[0, 2, 6, 9, 11, 15]
>>> longest_subsequence(seq, order='decreasing')
[12, 10, 9, 5, 3]
>>> txt = ("Given an input sequence, what is the best way to find the longest"
" (not necessarily continuous) non-decreasing subsequence.")
>>> ''.join(longest_subsequence(txt))
' ,abdegilnorsu'
>>> ''.join(longest_subsequence(txt, 'weak'))
' ceilnnnnrsssu'
>>> ''.join(longest_subsequence(txt, 'weakly', 'decreasing'))
'vuutttttttssronnnnngeee.'
>>> dates = [
... ('2015-02-03', 'name1'),
... ('2015-02-04', 'nameg'),
... ('2015-02-04', 'name5'),
... ('2015-02-05', 'nameh'),
... ('1929-03-12', 'name4'),
... ('2023-07-01', 'name7'),
... ('2015-02-07', 'name0'),
... ('2015-02-08', 'nameh'),
... ('2015-02-15', 'namex'),
... ('2015-02-09', 'namew'),
... ('1980-12-23', 'name2'),
... ('2015-02-12', 'namen'),
... ('2015-02-13', 'named'),
... ]
>>> longest_subsequence(dates, 'weak')
[('2015-02-03', 'name1'),
('2015-02-04', 'name5'),
('2015-02-05', 'nameh'),
('2015-02-07', 'name0'),
('2015-02-08', 'nameh'),
('2015-02-09', 'namew'),
('2015-02-12', 'namen'),
('2015-02-13', 'named')]
>>> from operator import itemgetter
>>> longest_subsequence(dates, 'weak', key=itemgetter(0))
[('2015-02-03', 'name1'),
('2015-02-04', 'nameg'),
('2015-02-04', 'name5'),
('2015-02-05', 'nameh'),
('2015-02-07', 'name0'),
('2015-02-08', 'nameh'),
('2015-02-09', 'namew'),
('2015-02-12', 'namen'),
('2015-02-13', 'named')]
>>> indices = set(longest_subsequence(dates, key=itemgetter(0), index=True))
>>> [e for i,e in enumerate(dates) if i not in indices]
[('2015-02-04', 'nameg'),
('1929-03-12', 'name4'),
('2023-07-01', 'name7'),
('2015-02-15', 'namex'),
('1980-12-23', 'name2')]
该答案部分是由于“ 代码审查”中的问题所启发,另一部分是由询问“无序”值的问题所启发。
添加回答
举报