@@ -75,6 +75,11 @@ fn parse_blocks(input: &str) -> Vec<Block> {
75
75
} else {
76
76
None
77
77
} ;
78
+
79
+ if num == 0 {
80
+ continue ;
81
+ }
82
+
78
83
let b = Block {
79
84
file_id,
80
85
length : num,
@@ -130,57 +135,78 @@ pub fn part_two(input: &str) -> Option<u64> {
130
135
// we want to move the files in order of decreasing file ID number
131
136
// 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
132
137
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
+ } ) ;
139
151
140
152
let mut right_block_idx = blocks. len ( ) - 1 ;
141
153
while right_block_idx > 0 {
142
- let right_block = & blocks[ right_block_idx] ;
154
+ let right_block = & blocks[ right_block_idx] . clone ( ) ;
143
155
if right_block. is_free ( ) {
144
156
right_block_idx -= 1 ;
145
157
continue ;
146
158
}
147
159
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;
159
166
}
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 ;
165
167
}
166
168
}
167
169
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
+ }
171
174
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;
178
200
}
201
+ space_list. insert ( insert_idx, new_idx) ;
179
202
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 ( ) ;
182
207
}
183
208
209
+ blocks[ right_block_idx] . file_id = None ;
184
210
right_block_idx -= 1 ;
185
211
}
186
212
@@ -214,4 +240,11 @@ mod tests {
214
240
) ) ;
215
241
assert_eq ! ( result, Some ( 2858 ) ) ;
216
242
}
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
+ }
217
250
}
0 commit comments