Find the Longest Increasing Subsequence of matches by new_idx. Given matches sorted by old_idx, find the longest subsequence where new_idx values are strictly increasing. This ensures the matches maintain relative order in both sequences. Uses the O(N log N) algorithm with binary search.
(matches: &[UniqueMatch])
| 225 | /// |
| 226 | /// Uses the O(N log N) algorithm with binary search. |
| 227 | fn longest_increasing_subsequence(matches: &[UniqueMatch]) -> Vec<UniqueMatch> { |
| 228 | if matches.is_empty() { |
| 229 | return Vec::new(); |
| 230 | } |
| 231 | |
| 232 | let n = matches.len(); |
| 233 | |
| 234 | // tails[i] = index in matches of the smallest ending element of all |
| 235 | // increasing subsequences of length i+1 |
| 236 | let mut tails: Vec<usize> = Vec::with_capacity(n); |
| 237 | |
| 238 | // predecessors[i] = index of the predecessor of matches[i] in the LIS |
| 239 | let mut predecessors: Vec<Option<usize>> = vec![None; n]; |
| 240 | |
| 241 | for i in 0..n { |
| 242 | let new_idx = matches[i].new_idx; |
| 243 | |
| 244 | // Binary search for the position to insert/replace |
| 245 | let pos = tails |
| 246 | .binary_search_by(|&j| matches[j].new_idx.cmp(&new_idx)) |
| 247 | .unwrap_or_else(|x| x); |
| 248 | |
| 249 | // Update predecessor |
| 250 | if pos > 0 { |
| 251 | predecessors[i] = Some(tails[pos - 1]); |
| 252 | } |
| 253 | |
| 254 | // Update tails |
| 255 | if pos == tails.len() { |
| 256 | tails.push(i); |
| 257 | } else { |
| 258 | tails[pos] = i; |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | // Reconstruct the LIS by following predecessors |
| 263 | let mut lis = Vec::with_capacity(tails.len()); |
| 264 | let mut current = tails.last().copied(); |
| 265 | |
| 266 | while let Some(idx) = current { |
| 267 | lis.push(matches[idx]); |
| 268 | current = predecessors[idx]; |
| 269 | } |
| 270 | |
| 271 | lis.reverse(); |
| 272 | lis |
| 273 | } |
| 274 | |
| 275 | /// Build the diff result using LIS anchors. |
| 276 | /// |