-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add problem 1948: Delete Duplicate Folders in System
- Loading branch information
Showing
3 changed files
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 147 additions & 0 deletions
147
src/problem_1948_delete_duplicate_folders_in_system/interning.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
pub struct Solution; | ||
|
||
// ------------------------------------------------------ snip ------------------------------------------------------ // | ||
|
||
use std::collections::hash_map::Entry; | ||
use std::collections::HashMap; | ||
use std::{convert, str}; | ||
|
||
type Name = [u8; 10]; | ||
|
||
#[derive(Default)] | ||
struct Node { | ||
children: HashMap<Name, Self>, | ||
signature_id: u32, | ||
} | ||
|
||
struct Context1 { | ||
signature_to_id: HashMap<Box<[(Name, u32)]>, u32>, | ||
id_to_duplication: Vec<bool>, | ||
} | ||
|
||
struct Context2<'a> { | ||
is_duplicated: Vec<bool>, | ||
path: Vec<&'a str>, | ||
result: Vec<Vec<String>>, | ||
} | ||
|
||
impl<'a> Context2<'a> { | ||
fn enter_path(&mut self, name: &'a Name, f: impl FnOnce(&mut Self)) { | ||
let length = name.iter().position(|&c| c == 0).unwrap_or(name.len()); | ||
let name = str::from_utf8(&name[..length]).unwrap(); | ||
|
||
self.path.push(name); | ||
|
||
f(self); | ||
|
||
self.path.pop(); | ||
} | ||
|
||
fn add_path_to_result(&mut self) { | ||
self.result | ||
.push(self.path.iter().copied().map(str::to_string).collect()); | ||
} | ||
} | ||
|
||
impl Solution { | ||
fn find_duplicates(context: &mut Context1, node: &mut Node) -> u32 { | ||
let signature_id = if node.children.is_empty() { | ||
u32::MAX | ||
} else { | ||
let mut signature = node | ||
.children | ||
.iter_mut() | ||
.map(|(&name, node)| (name, Self::find_duplicates(context, node))) | ||
.collect::<Box<_>>(); | ||
|
||
signature.sort_unstable_by_key(|&(name, _)| name); | ||
|
||
match context.signature_to_id.entry(signature) { | ||
Entry::Occupied(entry) => { | ||
let id = *entry.get(); | ||
|
||
context.id_to_duplication[id as usize] = true; | ||
|
||
id | ||
} | ||
Entry::Vacant(entry) => { | ||
let id = context.id_to_duplication.len() as _; | ||
|
||
entry.insert(id); | ||
context.id_to_duplication.push(false); | ||
|
||
id | ||
} | ||
} | ||
}; | ||
|
||
node.signature_id = signature_id; | ||
|
||
signature_id | ||
} | ||
|
||
fn collect_results<'a>(context: &mut Context2<'a>, nodes: &'a HashMap<Name, Node>) { | ||
for (name, child) in nodes { | ||
if let Some(&is_duplicated) = context.is_duplicated.get(child.signature_id as usize) { | ||
if !is_duplicated { | ||
context.enter_path(name, |context| { | ||
context.add_path_to_result(); | ||
|
||
Self::collect_results(context, &child.children); | ||
}); | ||
} | ||
} else { | ||
context.enter_path(name, Context2::add_path_to_result); | ||
} | ||
} | ||
} | ||
|
||
pub fn delete_duplicate_folder(paths: Vec<Vec<String>>) -> Vec<Vec<String>> { | ||
let mut root = Node::default(); | ||
|
||
for path in &paths { | ||
let mut node = &mut root; | ||
|
||
for component in path { | ||
let mut new_component = [0_u8; 10]; | ||
|
||
new_component[..component.len()].copy_from_slice(component.as_bytes()); | ||
|
||
node = node.children.entry(new_component).or_insert_with(Node::default); | ||
} | ||
} | ||
|
||
let mut cx1 = Context1 { | ||
signature_to_id: HashMap::new(), | ||
id_to_duplication: Vec::new(), | ||
}; | ||
|
||
Self::find_duplicates(&mut cx1, &mut root); | ||
|
||
let mut cx2 = Context2 { | ||
is_duplicated: convert::identity(cx1).id_to_duplication, | ||
path: Vec::new(), | ||
result: Vec::new(), | ||
}; | ||
|
||
Self::collect_results(&mut cx2, &root.children); | ||
|
||
cx2.result | ||
} | ||
} | ||
|
||
// ------------------------------------------------------ snip ------------------------------------------------------ // | ||
|
||
impl super::Solution for Solution { | ||
fn delete_duplicate_folder(paths: Vec<Vec<String>>) -> Vec<Vec<String>> { | ||
Self::delete_duplicate_folder(paths) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
#[test] | ||
fn test_solution() { | ||
super::super::tests::run::<super::Solution>(); | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
src/problem_1948_delete_duplicate_folders_in_system/mod.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
pub mod interning; | ||
|
||
pub trait Solution { | ||
fn delete_duplicate_folder(paths: Vec<Vec<String>>) -> Vec<Vec<String>>; | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::Solution; | ||
use crate::test_utilities; | ||
|
||
pub fn run<S: Solution>() { | ||
let test_cases = [ | ||
( | ||
&[&["a"] as &[_], &["c"], &["d"], &["a", "b"], &["c", "b"], &["d", "a"]] as &[&[_]], | ||
&[&["d"] as &[_], &["d", "a"]] as &[&[_]], | ||
), | ||
( | ||
&[ | ||
&["a"], | ||
&["c"], | ||
&["a", "b"], | ||
&["c", "b"], | ||
&["a", "b", "x"], | ||
&["a", "b", "x", "y"], | ||
&["w"], | ||
&["w", "y"], | ||
], | ||
&[&["a"], &["a", "b"], &["c"], &["c", "b"]], | ||
), | ||
( | ||
&[&["a", "b"], &["c", "d"], &["c"], &["a"]], | ||
&[&["a"], &["a", "b"], &["c"], &["c", "d"]], | ||
), | ||
]; | ||
|
||
for (paths, expected) in test_cases { | ||
assert_eq!( | ||
test_utilities::unstable_sorted(S::delete_duplicate_folder( | ||
paths | ||
.iter() | ||
.map(|account| account.iter().copied().map(str::to_string).collect()) | ||
.collect() | ||
)), | ||
expected, | ||
); | ||
} | ||
} | ||
} |