This looks like it's going to be one of the first more complex problems; it's a strange multi-step process to first move the blocks then produce the actual answer.
Just by peeking at the input, I can see that just blindly doing it like the example (just as a single full string) it's just going to end up with not enough IDs; so something a bit more involved might be required. Or maybe just having a series of ints.
After that... hmm.... check if the memory is compact, and if it's not:
- Find the first blank space
- Find the last filled space
- Swap the two
while (true)
{
int firstBlank = memory.TakeWhile(x => x >= 0).Count();
int lastFilledReverse = memory.AsEnumerable().Reverse().TakeWhile(x => x == -1).Count();
int lastFilled = memory.Count - 1 - lastFilledReverse;
if (lastFilled < firstBlank) break;
memory[firstBlank] = memory[lastFilled];
memory[lastFilled] = -1;
}
Linq is magic, I swear.
Anyway, that gets the answer, but it's.... slow. Very slow. It may have been faster to instead keep track of where blank spaces are in the first place, along with the size of the blank? Or maybe just count manually from where the last swap took place?
int firstBlank = 0;
int lastFilled = memory.Count - 1;
while (true)
{
while (memory[firstBlank] != -1) firstBlank++;
while (memory[lastFilled] == -1) lastFilled--;
// Rest of loop unchanged
}
Much faster! Sometimes trying to be clever is inefficient; just keep track of your work in progress. This went from taking 15,000ms to run, to about 1ms.
Part 2 is going to require a whole different kind of thinking. Instead of just having the whole memory in at once, It's going to require keeping track of the sizes of blocks and empty spaces. At the very least, each file is attempted to be moved only once; a small mercy instead of doing a full file defragmentation thing.
public int findBlank(List<int> memory, int size, int end)
{
var curSize = 0;
for (int i = 0; i < end; i++)
{
curSize = memory[i] == -1 ? curSize + 1 : 0;
if (curSize == size) return i - curSize + 1;
}
return -1;
}
for (int i = originalBlocks.Count - 1; i >= 0; i--)
{
var (blockStart, blockSize) = originalBlocks[i];
var blankSlot = findBlank(memory, blockSize, blockStart);
if (blankSlot == -1) continue;
for (int j = 0; j < blockSize; j++)
{
memory[blankSlot + j] = i;
memory[blockStart + j] = -1;
}
}
That seems to work! I was worried it would be pretty inefficient, and in a sense it is; however it still only took about 680ms to run, and under 1 full second is still pretty dang fast.