leetcode

Log | Files | Refs | README

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 }