Skip to content

Commit dd05502

Browse files
authored
Lis (#76)
* first draft of LIS * nit * add yosupo test * add helpful comment * rename * finish docs * allow pop * fix clippy * about to add another test * rng * now AC's * fix * fix * fix one comment * more nits * another nit * nit * saving * changing --------- Co-authored-by: Luke Videckis <lukevideckis@gmail.com>
1 parent c49eda6 commit dd05502

File tree

6 files changed

+202
-0
lines changed

6 files changed

+202
-0
lines changed

Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ edition = "2024"
1010
[dependencies]
1111
proconio = "0.4.5"
1212
ac-library-rs = "0.1.1"
13+
rand = "0.8.5"
1314

1415
[[example]]
1516
name = "binary_trie"
@@ -186,3 +187,15 @@ path = "examples/graphs/bridges_aizu.rs"
186187
[[example]]
187188
name = "ext_gcd"
188189
path = "examples/numbers/ext_gcd.rs"
190+
191+
[[example]]
192+
name = "lis_aizu"
193+
path = "examples/helpers/lis_aizu.rs"
194+
195+
[[example]]
196+
name = "lis_yosupo"
197+
path = "examples/helpers/lis_yosupo.rs"
198+
199+
[[example]]
200+
name = "lis_pop"
201+
path = "examples/helpers/lis_pop.rs"

examples/helpers/lis_aizu.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/problems/DPL_1_D
2+
3+
use proconio::input;
4+
use programming_team_code_rust::helpers::lis::Lis;
5+
6+
fn main() {
7+
input! {
8+
n: usize,
9+
a: [u32; n]
10+
}
11+
12+
let mut lis = Lis::default();
13+
14+
for elem in a {
15+
lis.push(elem);
16+
}
17+
18+
println!("{}", lis.dp.len());
19+
}

examples/helpers/lis_pop.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/all/ITP1_1_A
2+
3+
use programming_team_code_rust::helpers::lis::Lis;
4+
5+
fn lis_quadratic(a: &[i32]) -> usize {
6+
let n = a.len();
7+
if n == 0 {
8+
return 0;
9+
}
10+
let mut dp = vec![1; n];
11+
for i in 0..n {
12+
for j in 0..i {
13+
if a[j] < a[i] {
14+
dp[i] = dp[i].max(1 + dp[j]);
15+
}
16+
}
17+
}
18+
*dp.iter().max().unwrap()
19+
}
20+
21+
fn main() {
22+
for _ in 0..100 {
23+
let mut lis = Lis::default();
24+
let mut a = Vec::new();
25+
for _ in 0..1000 {
26+
match rand::random::<u8>() % 3 {
27+
0 => {
28+
let new_num = rand::random::<i32>();
29+
lis.push(new_num);
30+
a.push(new_num);
31+
}
32+
1 => {
33+
if !a.is_empty() {
34+
lis.pop();
35+
a.pop();
36+
}
37+
}
38+
_ => {
39+
let idxs = lis.get_lis();
40+
41+
assert_eq!(idxs.len(), lis_quadratic(&a));
42+
for i in 1..idxs.len() {
43+
assert!(a[idxs[i - 1]] < a[idxs[i]]);
44+
}
45+
}
46+
}
47+
}
48+
}
49+
50+
println!("Hello World");
51+
}

examples/helpers/lis_yosupo.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// verification-helper: PROBLEM https://judge.yosupo.jp/problem/longest_increasing_subsequence
2+
3+
use proconio::input;
4+
use programming_team_code_rust::helpers::lis::Lis;
5+
6+
fn main() {
7+
input! {
8+
n: usize,
9+
a: [i64; n]
10+
}
11+
12+
let mut lis = Lis::default();
13+
14+
for &elem in &a {
15+
lis.push(elem);
16+
}
17+
18+
let idxs = lis.get_lis();
19+
20+
println!("{}", idxs.len());
21+
println!(
22+
"{}",
23+
idxs.iter()
24+
.map(|&x| x.to_string())
25+
.collect::<Vec<_>>()
26+
.join(" ")
27+
);
28+
}

src/helpers/lis.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//! # Longest Increasing Subsequence
2+
3+
/// # Example
4+
/// ```
5+
/// use programming_team_code_rust::helpers::lis::Lis;
6+
///
7+
/// let a = [1, 1, -2, 1];
8+
///
9+
/// let mut lis = Lis::default();
10+
/// for &num in &a {
11+
/// lis.push(num);
12+
/// }
13+
///
14+
/// assert_eq!(lis.dp.len(), 2);
15+
/// assert_eq!(lis.get_lis(), [2, 3]);
16+
///
17+
/// lis.pop();
18+
///
19+
/// assert_eq!(lis.dp.len(), 1);
20+
/// assert_eq!(lis.get_lis(), [2]);
21+
/// ```
22+
#[derive(Default)]
23+
pub struct Lis<T> {
24+
/// dp\[i\].0 = smallest number such that there exists a LIS of length i+1 ending in this number
25+
/// dp\[i\].1 = index in original array of dp\[i\].0
26+
pub dp: Vec<(T, usize)>,
27+
#[allow(clippy::type_complexity)]
28+
st: Vec<(Option<usize>, Option<(usize, (T, usize))>)>,
29+
}
30+
31+
impl<T: Copy + Ord> Lis<T> {
32+
/// Pushes new_elem onto back of vec
33+
///
34+
/// # Complexity
35+
/// - n: length of vec
36+
/// - Time: O(log(LIS.len()))
37+
/// - Space: O(n) total
38+
pub fn push(&mut self, new_elem: T) {
39+
// change to `elem <= new_elem` for longest non-decreasing subsequence
40+
let idx = self.dp.partition_point(|&(elem, _)| elem < new_elem);
41+
let mut prev = None;
42+
if idx == self.dp.len() {
43+
self.dp.push((new_elem, self.st.len()));
44+
} else {
45+
prev = Some((idx, self.dp[idx]));
46+
self.dp[idx] = (new_elem, self.st.len());
47+
}
48+
self.st.push((
49+
match idx {
50+
0 => None,
51+
_ => Some(self.dp[idx - 1].1),
52+
},
53+
prev,
54+
));
55+
}
56+
57+
/// Pop off back of vec
58+
///
59+
/// # Complexity
60+
/// - Time: O(1)
61+
/// - Space: O(1)
62+
pub fn pop(&mut self) {
63+
let (_, prev) = self.st.pop().unwrap();
64+
if let Some((idx, prev)) = prev {
65+
self.dp[idx] = prev;
66+
} else {
67+
self.dp.pop();
68+
}
69+
}
70+
71+
/// Gets indexes of LIS of vec
72+
///
73+
/// # Complexity
74+
/// - Time: O(LIS.len())
75+
/// - Space: O(LIS.len())
76+
pub fn get_lis(&self) -> Vec<usize> {
77+
if self.dp.is_empty() {
78+
return Vec::new();
79+
}
80+
let mut idxs = Vec::with_capacity(self.dp.len());
81+
let mut idx = self.dp.last().unwrap().1;
82+
idxs.push(idx);
83+
while let Some(prev) = self.st[idx].0 {
84+
idx = prev;
85+
idxs.push(idx);
86+
}
87+
idxs.reverse();
88+
idxs
89+
}
90+
}

src/helpers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
//! # Helpers
22
pub mod compress;
3+
pub mod lis;
34
pub mod unsafe_recursive_closure;

0 commit comments

Comments
 (0)