longest_common_prefix.rs (6280B)
1 //! Write a function to find the longest common prefix string amongst 2 //! an array of strings. 3 //! 4 //! If there is no common prefix, return an empty string "". 5 6 use std::collections::{BTreeMap, HashMap, HashSet}; 7 8 use tracing::info; 9 10 // First manual attempt - the ugliest solution that worked 11 // 255ms, 2.40mb 12 fn solution_1(words: Vec<String>) -> String { 13 let mut prefixes = HashMap::<usize, char>::new(); 14 let mut matches = Vec::<(usize, char)>::new(); 15 16 let mut final_results = Vec::<(usize, char)>::new(); 17 18 for word in words.iter() { 19 info!("{word}"); 20 for (idx, char) in word.chars().enumerate() { 21 info!("{idx}: {char}"); 22 prefixes.insert(idx, char); 23 } 24 } 25 26 for word in words.iter() { 27 for (index, prefix) in prefixes.clone() { 28 let mut current = word.char_indices(); 29 let result = current.find(|(idx, char)| idx.eq(&index) && char.eq(&prefix)); 30 if let Some(found) = result { 31 info!("Match found: {index}: {prefix}: {found:?}"); 32 matches.push((index, prefix)); 33 } else { 34 info!("No match found"); 35 } 36 } 37 } 38 39 for char in matches.iter() { 40 let count = matches.iter().filter(|x| x.eq(&char)).count(); 41 42 if count.eq(&words.len()) && !final_results.contains(char) { 43 final_results.push(*char); 44 } 45 } 46 47 final_results.sort_by_key(|x| x.0); 48 49 info!("Final results: {final_results:?}"); 50 51 let mut prev: usize = 0; 52 final_results.retain(|(index, _)| { 53 if index.eq(&0) { 54 return true; 55 } 56 if index.eq(&(prev + 1)) { 57 prev = *index; 58 true 59 } else { 60 false 61 } 62 }); 63 64 info!("Retained results: {final_results:?}"); 65 66 let has_zero = final_results.iter().any(|(idx, _)| idx.eq(&0)); 67 if !has_zero { 68 return String::new(); 69 } 70 71 let result = String::from_iter(final_results.iter().map(|x| x.1)); 72 info!("Result: {result}\n\n"); 73 result 74 } 75 76 // Second try 77 // 115ms, 2.41mb 78 fn solution_2(strs: Vec<String>) -> String { 79 let mut common = Vec::<(usize, char)>::new(); 80 81 let characters = strs 82 .iter() 83 .flat_map(|x| x.char_indices()) 84 .collect::<Vec<(usize, char)>>(); 85 info!("characters: {characters:?}"); 86 87 for (index, character) in characters { 88 info!("Index: {index} - Character: {character}"); 89 let prefix = String::from_iter(common.iter().map(|x| x.1)); 90 let all_starts_with = strs 91 .iter() 92 .all(|x| x.starts_with(&format!("{prefix}{character}"))); 93 info!("Does all words starts with {prefix}? {all_starts_with}"); 94 let contains = strs.iter().all(|x| x.contains(character)); 95 if contains && all_starts_with { 96 if common.is_empty() && index.eq(&0) { 97 common.push((index, character)); 98 } else { 99 if let Some(last) = common.last() { 100 info!("previous: {last:?}"); 101 if last.0 + 1 == index { 102 common.push((index, character)); 103 } 104 } 105 } 106 } 107 } 108 109 info!("Common: {common:?}"); 110 let prefix = String::from_iter(common.iter().map(|x| x.1)); 111 info!("PREFIX: {prefix}"); 112 prefix 113 } 114 115 // Third try: BTreeMap is automatically sorted 116 // 0ms, 2.33mb 117 fn solution_3(strs: Vec<String>) -> String { 118 let mut common = Vec::<char>::new(); 119 120 for (index, character) in strs 121 .iter() 122 .flat_map(|x| x.char_indices()) 123 .collect::<BTreeMap<usize, char>>() 124 { 125 info!("{index}: {character}"); 126 let existing_prefix = String::from_iter(&common); 127 let prefix = format!("{existing_prefix}{character}"); 128 if strs.iter().all(|x| x.starts_with(&prefix)) { 129 common.push(character); 130 } 131 } 132 133 info!("Common: {common:?}"); 134 135 String::from_iter(common) 136 } 137 138 // Fourth try: 139 // Can we use less than 2.1mb memory? 140 fn solution_4(strs: Vec<String>) -> String { 141 String::new() 142 } 143 144 #[cfg(test)] 145 mod tests { 146 use crate::{ 147 array::longest_common_prefix::{solution_1, solution_2, solution_3, solution_4}, 148 test_utils::benchmark::init_benchmark_tracing, 149 }; 150 151 struct Case { 152 input: Vec<String>, 153 output: String, 154 } 155 156 fn full_cases() -> Vec<Case> { 157 vec![ 158 Case { 159 input: vec![ 160 "flower".to_string(), 161 "flow".to_string(), 162 "flight".to_string(), 163 ], 164 output: "fl".to_string(), 165 }, 166 Case { 167 input: vec!["dog".to_string(), "racecar".to_string(), "car".to_string()], 168 output: String::new(), 169 }, 170 Case { 171 input: vec!["cir".to_string(), "car".to_string()], 172 output: "c".to_string(), 173 }, 174 Case { 175 input: vec!["babb".to_string(), "caa".to_string()], 176 output: String::new(), 177 }, 178 Case { 179 input: vec![ 180 "reflower".to_string(), 181 "flow".to_string(), 182 "flight".to_string(), 183 ], 184 output: String::new(), 185 }, 186 Case { 187 input: vec!["aa".to_string(), "aa".to_string()], 188 output: "aa".to_string(), 189 }, 190 Case { 191 input: vec!["aa".to_string(), "ab".to_string()], 192 output: "a".to_string(), 193 }, 194 ] 195 } 196 197 fn run_solver_cases(solver: fn(Vec<String>) -> String, cases: Vec<Case>) { 198 init_benchmark_tracing(); 199 for case in cases { 200 assert_eq!(solver(case.input), case.output); 201 } 202 } 203 204 // #[test] 205 // fn solver_1() { 206 // run_solver_cases(solution_1, full_cases()); 207 // } 208 209 // #[test] 210 // fn solver_2() { 211 // run_solver_cases(solution_2, full_cases()); 212 // } 213 214 #[test] 215 fn solver_3() { 216 run_solver_cases(solution_3, full_cases()); 217 } 218 219 #[test] 220 fn solver_4() { 221 run_solver_cases(solution_4, full_cases()); 222 } 223 }