@@ -75,6 +75,11 @@ fn parse_blocks(input: &str) -> Vec<Block> {
7575 } else {
7676 None
7777 } ;
78+
79+ if num == 0 {
80+ continue ;
81+ }
82+
7883 let b = Block {
7984 file_id,
8085 length : num,
@@ -130,57 +135,78 @@ pub fn part_two(input: &str) -> Option<u64> {
130135 // we want to move the files in order of decreasing file ID number
131136 // if there is no span of free space to the left of a file that is large enough to fit the file, the file does not move
132137
133- let mut spaces_block_indices: Vec < usize > = Vec :: with_capacity ( 1000 ) ;
134- for ( i, b) in blocks. iter ( ) . enumerate ( ) {
135- if b. file_id . is_none ( ) {
136- spaces_block_indices. push ( i) ;
137- }
138- }
138+ let mut spaces_block_indices_by_size: Vec < Vec < usize > > =
139+ ( 0 ..10 ) . map ( |_| Vec :: with_capacity ( 500 ) ) . collect ( ) ;
140+
141+ // store indices smaller last so we can pop from the end and don't need to shift that much
142+ blocks
143+ . iter ( )
144+ . enumerate ( )
145+ . rev ( )
146+ . filter ( |( _, b) | b. file_id . is_none ( ) )
147+ . filter ( |( _, b) | b. length > 0 )
148+ . for_each ( |( i, b) | {
149+ spaces_block_indices_by_size[ b. length as usize ] . push ( i) ;
150+ } ) ;
139151
140152 let mut right_block_idx = blocks. len ( ) - 1 ;
141153 while right_block_idx > 0 {
142- let right_block = & blocks[ right_block_idx] ;
154+ let right_block = & blocks[ right_block_idx] . clone ( ) ;
143155 if right_block. is_free ( ) {
144156 right_block_idx -= 1 ;
145157 continue ;
146158 }
147159
148- // look for a free space block that can fit the file
149- let mut free_space_block_idx = None ;
150- for ( i, space_idx) in spaces_block_indices. iter ( ) . enumerate ( ) {
151- if * space_idx > right_block_idx {
152- break ;
153- }
154- let left_block = & blocks[ * space_idx] ;
155- if left_block. length > right_block. length {
156- free_space_block_idx = Some ( * space_idx) ;
157- for i in i..spaces_block_indices. len ( ) {
158- spaces_block_indices[ i] += 1 ;
160+ // look for the best free space block that can fit the file, only looking at the last of each size
161+ let mut best_free_space_block_idx = usize:: MAX ;
162+ for s in ( right_block. length as usize ) ..spaces_block_indices_by_size. len ( ) {
163+ if let Some ( & found_idx) = spaces_block_indices_by_size[ s] . last ( ) {
164+ if found_idx < right_block_idx && found_idx < best_free_space_block_idx {
165+ best_free_space_block_idx = found_idx;
159166 }
160- break ;
161- } else if left_block. length == right_block. length {
162- free_space_block_idx = Some ( * space_idx) ;
163- spaces_block_indices. remove ( i) ;
164- break ;
165167 }
166168 }
167169
168- if let Some ( left_block_idx) = free_space_block_idx {
169- let left_block = & blocks[ left_block_idx] ;
170- let ( _, maybe_left_additional_space) = left_block. split ( right_block. length ) ;
170+ if best_free_space_block_idx == usize:: MAX {
171+ right_block_idx -= 1 ;
172+ continue ;
173+ }
171174
172- if let Some ( left_additional_space) = maybe_left_additional_space {
173- blocks. insert ( left_block_idx, right_block. clone ( ) ) ;
174- blocks[ left_block_idx + 1 ] = left_additional_space;
175- right_block_idx += 1 ;
176- } else {
177- blocks[ left_block_idx] = right_block. clone ( ) ;
175+ let left_block_idx = best_free_space_block_idx;
176+ let left_block = & blocks[ left_block_idx] ;
177+ spaces_block_indices_by_size[ left_block. length as usize ] . pop ( ) ;
178+
179+ let ( _, maybe_left_additional_space) = left_block. split ( right_block. length ) ;
180+ if let Some ( left_additional_space) = maybe_left_additional_space {
181+ let new_idx = left_block_idx + 1 ;
182+ blocks. insert ( left_block_idx, right_block. clone ( ) ) ;
183+
184+ // shift all the indices that are greater than the left block index
185+ spaces_block_indices_by_size
186+ . iter_mut ( )
187+ . flat_map ( |space_list| space_list. iter_mut ( ) )
188+ . filter ( |idx| * * idx > left_block_idx)
189+ . for_each ( |idx| * idx += 1 ) ;
190+
191+ // add the new space to the list
192+ let space_list =
193+ & mut spaces_block_indices_by_size[ left_additional_space. length as usize ] ;
194+ let mut insert_idx = space_list. len ( ) ;
195+ for i in ( 0 ..space_list. len ( ) ) . rev ( ) {
196+ if space_list[ i] > new_idx {
197+ break ;
198+ }
199+ insert_idx = i;
178200 }
201+ space_list. insert ( insert_idx, new_idx) ;
179202
180- blocks[ right_block_idx] . file_id = None ;
181- right_block_idx -= 1 ;
203+ blocks[ new_idx] = left_additional_space;
204+ right_block_idx += 1 ;
205+ } else {
206+ blocks[ left_block_idx] = right_block. clone ( ) ;
182207 }
183208
209+ blocks[ right_block_idx] . file_id = None ;
184210 right_block_idx -= 1 ;
185211 }
186212
@@ -214,4 +240,11 @@ mod tests {
214240 ) ) ;
215241 assert_eq ! ( result, Some ( 2858 ) ) ;
216242 }
243+
244+ #[ test]
245+ fn test_part_two_3 ( ) {
246+ let input = "55341271410101" ;
247+ let result = part_two ( & input) ;
248+ assert_eq ! ( result, Some ( 638 ) ) ;
249+ }
217250}
0 commit comments