1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright 2018-2019 Joe Neeman.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// See the LICENSE-APACHE or LICENSE-MIT files at the top-level directory
// of this distribution.
use usize;
// Here, we implement the "patience" algorithm for the longest increasing subsequence. In our
// applications of this algorithm, we always expect the sequence to have unique elements, but in
// any case this function will compute the longest *strictly* increasing subsequence.
//
// Here's a quick overview of the algorithm. We maintain a bunch of "stacks," starting with a
// single stack on the left, and adding extra stacks to the right as necessary. Each stack will be
// (non-strictly) ordered, with largest values on the bottom and smaller values on top. We process
// the input sequence from beginning to end, we put it on the left-most stack possible, keeping in
// mind that each stack needs to stay ordered. If we can't put in on any stack, we start a new
// stack on the right.
//
// Here's a quick example: if the input sequence is 4 6 5 3 7, then in the first step we put 4 in its
// own stack:
//
// ```
// 4
// ```
//
// The 6 cannot go on top of the 4, so it starts a new stack.
//
// ```
// 4 6
// ```
//
// The left-most legal position for the 5 is on top of the 6.
//
// ```
// 5
// 4 6
// ```
//
// The 7 goes in its own stack:
//
// ```
// 5
// 4 6 7
// ```
//
// Finally, the 3 goes on top of the 4:
//
// ```
// 3 5
// 4 6 7
// ```
//
// To reconstruct a longest increasing subsequence (LIS), start with the right-most stack. Take the
// element on top (7, in the example above); this will be the last element of the LIS. To find the
// preceeding (second-last) element of the LIS, we rewind back to the time when the last element
// was placed in its stack, and we look at the top of the stack to its left (which, in the example
// above, was a 5). Then we repeat: at the time the 5 was placed, the top of the stack to its left
// held a 4. That is, 4 5 7 is an LIS of the original sequence.
//
// In order to translate this into a more efficient algorithm, we make two observations.
//
// 1. We don't need to record the entire history of the process; instead, we just keep track of the
// part of the history that we need: every time we add an element to a stack, record that
// element and the element at the top of the stack to the left.
// 2. We don't even need to store all of the stacks; it's enough to just store the top element of
// each stack. Also, note that the sequence of top elements forms an increasing sequence, and so
// each time we process an element, we can use binary search to figure out where it should go.
//
// There's one other way in which the implementation below differs from the description above:
// rather than manipulate the sequence elements directly, we work with indices pointing to them.
// This avoids the need to assume that `T` is cloneable (and in case `T` is large, it might be
// faster).