Dumb order of operations mistake got me to this point 🙃was missing the parens around the last half of the conditional since like 12:20. This mirrors the classic stack match parentheses problem.
My first inclination was to do this recursively. Had to fudge it a bit because doing a truly recursive solution exceeds the max recursive depth :(
Here's my Python for part 1. It's very slow. Gonna look into refactoring to a regex or list comprehension solution for part 2. Kind of ready to move out of string manipulation land either way!
Wow, that stack thing is clever! It didn't even occur to me that you could evaluate as you go in a single pass through the polymer. Implementing the stack in c# (from the impl i posted below) went from ~2:50 => 268ms :O
Maybe most already know... But, parsing the polymer string as a string and using str.replace twice as you do is still much faster than using polymer as a list and using a list-comprehension to remove units (35% slower).
Wow, I did not see the stack-based solution in there at all. I tried the recursive approach and then one similar to @thejessleigh and stopped there! At least I enjoyed this one more than Day 4!
I went with RegExp solution. I created a regexp for all the reactions and delete everyting, then iterate until no more matches
constpolymer=readFileSync('./data',{encoding:'utf8'}).trim()// A-ZconstupperLetters=[...Array(26)].map((_,i)=>String.fromCharCode(65+i))// A RegExp of the form Aa|...|Zz|aA|...|zZconstregexp=newRegExp(upperLetters.map(upper=>`${upper}${upper.toLocaleLowerCase()}`).concat(upperLetters.map(upper=>`${upper.toLocaleLowerCase()}${upper}`)).join("|"),"g")// while there is a match for the regexp replace all and retry. returns the legnthconstreactToPolymer=polymerToReact=>{while(regexp.test(polymerToReact)){polymerToReact=polymerToReact.replace(regexp,"")}returnpolymerToReact.length}console.log(reactToPolymer(polymer))
this one was easy (to me!) in comparison to yesterdays, might even be the easiest thus far. All that being said, DAMNNNNNN is part 2 slow, at least with my current impl.
00:07 run time for part 1 02:50 run time for part 2 :OOOOO
publicclassAlchemicalReduction{publicstringReducePolymer(stringpolymer){while(true){StringBuildersb=newStringBuilder();varpreviousChar="";for(inti=0;i<polymer.Length;i++){stringcurrentChar=newstring(new[]{polymer[i]});// Same letterif(currentChar.Equals(previousChar,StringComparison.OrdinalIgnoreCase)){// different caseif(!currentChar.Equals(previousChar,StringComparison.Ordinal)){// Remove the last character from the builder, don't add this charactersb.Remove(i-1,1);// Add the remaining characters to the buildersb.Append(polymer.Substring(i+1,polymer.Length-i-1));// reset the previous char for next entry into for looppreviousChar="";break;}}// Record new previous char, append the current char to the builderpreviousChar=currentChar;sb.Append(currentChar);}// Completed for loo pass, build the stringvarnewString=sb.ToString();// break out of while loop if they're the same string (has been reduced by maximum amount)if(polymer==newString){break;}// Work with the newly modified string within the for looppolymer=newString;}returnpolymer;}publicstringFullyReducePolymerByEliminatingSingleUnit(stringpolymer){varoriginalPolymer=polymer;varnormalizedPolymer=originalPolymer.ToUpper();// get the individual "types" within the polymervargroupsOfTypes=normalizedPolymer.GroupBy(gb=>gb).Select(s=>s.Key);// builds new list of potential polymers, removing a single type from the chain in each runList<string>newPotentialPolymers=newList<string>();foreach(varsingroupsOfTypes){// Removes a single type from the potential polymervarcharToRemove=newstring(new[]{s});varregex=newRegex(charToRemove,RegexOptions.IgnoreCase);newPotentialPolymers.Add(regex.Replace(originalPolymer,""));}// Run the new potential polymersList<string>reducedPolymers=newList<string>();foreach(varpotentialPolymerinnewPotentialPolymers){reducedPolymers.Add(ReducePolymer(potentialPolymer));}// return the smallest onevarminLengthPolymer=reducedPolymers.Min(m=>m.Length);returnreducedPolymers.Where(w=>w.Length==minLengthPolymer).First();}}
Didn't think about the stack approach til seeing other's solutions, wow so much faster! Didn't really occur to me you could accomplish the whole reduction in a single pass through the polymer.
Part 1 went from 00:07 -> 12ms Part 2 went from 02:50 -> 269ms
publicstringReducePolymer(stringpolymer){// Refactored to stack with input from others solutions at: // https://dev.to/rpalo/aoc-day-5-alchemical-reduction-5b2f/commentsStack<string>stack=newStack<string>();foreach(varcinpolymer){vars=newstring(new[]{c});// the top item in the stack (or empty string if we haven't yet added to stack)vartop=stack.Count==0?string.Empty:stack.Peek();// if the top item is the same letter, // but different case than the currently evaluated character,// remove the top item from the stack, and don't add the current character.if(top!=""&&top.ToLower()==s.ToLower()&&top!=s){stack.Pop();}// No match, add the character to the stackelse{stack.Push(s);}}// Is there a better way to project the stack back into a contiguous string?varsb=newStringBuilder();while(stack.Count>0){sb.Append(stack.Pop());}returnsb.ToString();}
I wrote this solution pretty quickly... and then kept having the wrong value.
I went down a rabbit hole involving hexdump and such because I was pretty confident in what the code was supposed to do and didn't see a valid result come up. It drove me crazy, but finally after way more time digging than I'd like to admit, here's my solution:
importFsfrom"fs"importPathfrom"path"constCASE_ASCII_OFFSET=32letinput=Fs.readFileSync(Path.join(__dirname,"input.txt")).toString().trim()for(leti=0;i<input.length;i++){constcurrentValue=input.charAt(i)constnextValue=input.charAt(i+1)// reached the endif(nextValue===undefined){continue}constisSameTypeAndOppositePolarity=Math.abs(currentValue.charCodeAt(0)-nextValue.charCodeAt(0))===CASE_ASCII_OFFSETif(isSameTypeAndOppositePolarity){input=input.slice(0,i)+input.slice(i+2)// Coming back to previous position but since it's going to be// incremented by the for loop, let's take a supplementary stepi=Math.max(-1,i-2)}}console.log(input.length)
I'll try and find some time to write up what problem I encountered
Ryan is an engineer in the Sacramento Area with a focus in Python, Ruby, and Rust. Bash/Python Exercism mentor. Coding, physics, calculus, music, woodworking. Looking for work!
I originally had an iterative, while-loopy, pointer-based solution, and that ran OK, but like a lot of people, I realized that it's basically an extended version of the "Matching Parentheses" problem, and that a stack would let me iterate through the list more with less backward steps. Now it runs pretty quick!
Rust's lack of easy handling of upper- and lowercase characters kind of bummed me out.
Part 1
/// Day 5: Alchemical Reductionusestd::collections::HashSet;// Part 1: How many units remain after fully reacting a polymer/// Reduce down a polymer by dropping any mer pairs/// /// A mer pair is any lowercase letter and its corresponding uppercase/// letter, adjacent to each other/// Optionally, provide a "drop_mer" to drop unconditionally, case insenitive/// pair or notpubfnreduce(polymer:&str,drop_mer:Option<char>)->String{letmutresult:Vec<char>=Vec::new();forcinpolymer.chars(){ifmatches_drop_mer(c,drop_mer){continue;}ifresult.last().is_some()&&is_polymer_pair(*result.last().unwrap(),c){result.pop();}else{result.push(c);}}result.into_iter().collect()}fnis_polymer_pair(first:char,second:char)->bool{(first.is_lowercase()&&second.is_uppercase()&&second.to_lowercase().next().unwrap()==first)||(first.is_uppercase()&&second.is_lowercase()&&second.to_uppercase().next().unwrap()==first)}fnmatches_drop_mer(c:char,drop_mer:Option<char>)->bool{drop_mer.is_some()&&c.to_lowercase().next().unwrap()==drop_mer.unwrap().to_lowercase().next().unwrap()}
Part 2
For part 2, I didn't iterate over all 26 letters of the alphabet no matter what, I used a set to figure out what characters were in the input and only loop over those. In this case, it doesn't help, because the input has all 26 anyhow. Oh well. I learned a lot about .collect() today.
// Part 2: Figure out which polymer, when removed, allows the most// compacting, remove it, and return the length of the shortest polymer// after compaction./// Optimizes a polymer by figuring out which *one* mer is inhibiting/// reduction the most and removing it. The reduced string is returnedpubfnoptimize(polymer:&str)->String{letpossible_mers:HashSet<char>=polymer.to_lowercase().chars().collect();letmutresult_candidates:Vec<String>=Vec::new();formerinpossible_mers.into_iter(){result_candidates.push(reduce(polymer,Some(mer)));}result_candidates.into_iter().min_by_key(|candidate|candidate.len()).expect("No result candidates found.")}
never met a part of the stack I didn't like. sr. engineer at clique studios in chicago, perpetual creative hobbyist, bird friend, local gay agenda promoter. she/her. tips: https://ko-fi.com/carlymho
Second part looks kind of long because I figured it would be faster to just declare the letters rather than generate the letters programmatically. I'm pretty sure there's a way to improve performance here, since this has a long-ish runtime, but I wasn't up to optimizing performance at midnight, haha. I might fiddle with this some more to see if I can do better.
I am using Advent of Code to learn Golang, and here is the solution I came up with. Suggestions for improvements are always welcome!
I was first using a list which would go from left to right and remove pairs as they were found reacting, shifting one step to the left and continue finding pairs that react. However, this could be simplified by using Ali Spittel's stack solution in Python.
With this inspiration, here is my Golang solution. I have added timing functions just to print out the running time as I have compared them to Python it is finally a speed up using Golang for the solution in this case!
packagemainimport("bufio""fmt""os""strings""time""unicode")// readLines reads a whole file into memory// and returns a slice of its lines.funcreadLines(pathstring)([]string,error){file,err:=os.Open(path)iferr!=nil{returnnil,err}deferfile.Close()varlines[]stringscanner:=bufio.NewScanner(file)forscanner.Scan(){lines=append(lines,scanner.Text())}returnlines,scanner.Err()}funcinvertChar(crune)rune{ifunicode.ToUpper(c)==c{returnunicode.ToLower(c)}returnunicode.ToUpper(c)}funcreact(polymerstring)int{stack:=[]rune{}vartoprunefor_,unit:=rangepolymer{iflen(stack)==0{// Initialise stack:stack=append(stack,unit)continue}top=stack[len(stack)-1]iftop==invertChar(unit){// Consume last unit:stack=stack[:len(stack)-1]}else{stack=append(stack,unit)}}returnlen(stack)}funcmain(){data,err:=readLines("input")iferr!=nil{panic(err)}polymerString:=data[0]// Part 1start:=time.Now()fmt.Println(react(polymerString))elapsed:=time.Since(start)fmt.Println(elapsed)// Part 2:start=time.Now()varreducedstringminReact:=-1charSet:="ABCDEFGHIJKLMNOPQRSTUVXYZ"for_,c:=rangecharSet{reduced=strings.Replace(polymerString,string(c),"",-1)reduced=strings.Replace(reduced,string(unicode.ToLower(c)),"",-1)r:=react(reduced)ifminReact==-1||r<minReact{minReact=r}}fmt.Println(minReact)elapsed=time.Since(start)fmt.Println(elapsed)}
Ryan is an engineer in the Sacramento Area with a focus in Python, Ruby, and Rust. Bash/Python Exercism mentor. Coding, physics, calculus, music, woodworking. Looking for work!
A nice easy one! Just fold into a stack and bada-bing-bada-boom!
Part 1
I started with String and String.last. This got me the solution, but it turned out to be really slow. Here's my updated code using a Java LinkedList instead, which is multiple orders of magnitude faster.
This one seemed a little too simple at first, but my brute force implementation worked in about a 1500ms (still noticeably slow). After checking that my answer was correct, I did the stack optimization above which pulled me back down to 80ms or so.
For this solution I used a stack. If the unit on top of the stack can react to the incoming unit, I just pop it out of the stack. Otherwise, I push it and move on.
const{readFile}=require('./reader');const{reactPolymer}=require('./05-common');(async()=>{constlines=awaitreadFile('05-input.txt');constpolymer=reactPolymer(lines[0]);console.log(`The remaining units are ${polymer.length}`);})();
05b.js
const{readFile}=require('./reader');const{reactPolymer}=require('./05-common');constdetectUnitTypes=polymer=>{constexistence=newSet();returnpolymer.toLowerCase().split('').filter(unit=>{if(existence.has(unit)){returnfalse;}existence.add(unit);returntrue;});};(async()=>{constlines=awaitreadFile('05-input.txt');constunitTypes=detectUnitTypes(lines[0]);constpolymersWithoutUnit=newMap();for(letunitofunitTypes){constpolymer=reactPolymer(lines[0].replace(newRegExp(unit,'ig'),''));polymersWithoutUnit.set(unit,polymer.length);}constshortestPolymerLength=Math.min(...polymersWithoutUnit.values());console.log(`The shortest polymer length is ${shortestPolymerLength}`);})();
I actually managed to complete Part A with an O(n2) algorithm, before realizing that would be too slow for part B...then I realized I could use a stack to implement an O(n) algorithm.
function reduction(chain)chain=reduction_one_pass(chain)returnlength(chain)endfunction reduction_one_pass(chain)result=[chain[1]]i=2whilei<=length(chain)a=result[end]b=chain[i]i+=1whilea!=b&&uppercase(a)==uppercase(b)deleteat!(result,length(result))# remove a# get new a,biflength(result)>0a=result[end]elsea=""endb=chain[i]i+=1end# b doesn't match last char on stackpush!(result,b)endreturnjoin(result,"")endfunction main()filename=ARGS[1]# julia arrays are 1-indexed!input=split(read(filename,String),"\n")[1]test_input="dabAcCaCBAcCcaDA"println(reduction(input))endmain()
Part B is iterating through all letters in the alphabet, doing the string replace and then, doing the same reduction as Part A, while tracking the minimum reduced length. I feel like there's a better way to do that, but...never figured it out.
function find_biggest_reduction(chain)smallest=-1# there's gotta be a pattern here but I can't quite figure it out# so we're brute forcing itchar_A=Int("a"[1])foriinchar_A:char_A+25letter=Char(i)sub=replace(replace(chain,letter=>""),uppercase(letter)=>"")reduced=reduction_one_pass(sub)println("$letter -> length: $(length(reduced))")iflength(reduced)<smallest||smallest==-1smallest=length(reduced)endendreturnsmallestend
I logged my output and saw that, for my input, v reduced far better than anything else.
a -> length: 10432 b -> length: 10448 c -> length: 10510 d -> length: 10484 e -> length: 10450 f -> length: 10528 g -> length: 10424 h -> length: 10490 i -> length: 10480 j -> length: 10480 k -> length: 10444 l -> length: 10386 m -> length: 10426 n -> length: 10454 o -> length: 10476 p -> length: 10412 q -> length: 10476 r -> length: 10420 s -> length: 10426 t -> length: 10452 u -> length: 10456 v -> length: 4684 w -> length: 10468 x -> length: 10366 y -> length: 10476 z -> length: 10486
My gut tells me there's some pattern in the input that would tell you which char to remove, and that way you only have to do the reduction once (as opposed to 26 times).
Ryan is an engineer in the Sacramento Area with a focus in Python, Ruby, and Rust. Bash/Python Exercism mentor. Coding, physics, calculus, music, woodworking. Looking for work!
Probably won't get an explanatory blog post on this one today, and need to catch up to finish/submit yesterday's, but here's my Clojure solution for Day 5 (see gist.github.com/ballpointcarrot/7e...
(nsaoc.aoc5)(defnpolymer-drop[[c1c2]](cond(=c1c2)false(or(nil?c1)(nil?c2))false(=(Character/toLowerCasec1)(Character/toLowerCasec2))true:elsefalse))(defnshrink[input](loop[shrunk[]chars-to-test(take2input)left(drop2input)](cond(and(empty?left)(every?nil?chars-to-test))(applystrshrunk)(nil?(firstchars-to-test))(recurshrunk[(lastchars-to-test)(firstleft)](restleft))(polymer-dropchars-to-test)(if(empty?shrunk)(recurshrunk(take2left)(drop2left))(recur(popshrunk)[(lastshrunk)(firstleft)](restleft))):else(recur(conjshrunk(firstchars-to-test))[(lastchars-to-test)(firstleft)](restleft)))))(defnremove-char"Remove all instances of a character (case-insensitive) from a string"[stringchr](applystr(remove#(or(=%(Character/toUpperCasechr))(=%(Character/toLowerCasechr)))string)))(defnchar-range[startend](mapchar(range(intstart)(inc(intend)))))(defnfind-shortest-polymer[input-string](applymin(pmap#(->input-string(remove-char%)(shrink)(count))(char-range\a\z))))
Ryan is an engineer in the Sacramento Area with a focus in Python, Ruby, and Rust. Bash/Python Exercism mentor. Coding, physics, calculus, music, woodworking. Looking for work!
My day 5 solutions in Elixir. I'm actually pretty happy with how the code turned out this time, might be the cleanest of all days up until now.
The main idea of the implementation of part one is to put characters on a stack, and then compare the head of the stack with the next element to determine if the head and the new element should stay or go.
Part two is a wrapper around the first part, where the input is preprocessed (e.g. units are removed) before being fed to the implementation of the first part. The units to remove are based on unicode numbers.
Ugh, I hated this one, mainly because I took a completely wrong approach at first in an effort to be very fast. It was fast alright but could I get it to produce the right answer?!
Late, but very happy with this one and had a lot of fun
Node.js
constisUpperCase=letter=>letter===letter.toUpperCase();constisLowerCase=letter=>letter===letter.toLowerCase();constlettersAreEqual=(a,b)=>a.toUpperCase()===b.toUpperCase();constlast=array=>array[array.length-1];constunique=array=>[...newMap(array.map(s=>[s.toLowerCase(),s])).values()];constdoesReact=(a,b)=>{letreacts=false;if((isLowerCase(a)&&isUpperCase(b))||(isUpperCase(a)&&isLowerCase(b))){if(lettersAreEqual(a,b)){reacts=true;}}returnreacts;};constremovePolarity=polymer=>{polymer=[...polymer];constoutput=[""];for(constcharofpolymer){if(doesReact(char,last(output))){output.pop();}else{output.push(char);}}// minus one for the emptry string at the startreturnoutput.length-1;};constbestPolarity=polymer=>{polymer=[...polymer];constuniqueLetters=unique(polymer);constresults=uniqueLetters.map(letter=>{conststrippedPolymer=polymer.filter(c=>!lettersAreEqual(c,letter));returnremovePolarity(strippedPolymer);});returnMath.min.apply(null,results);};
I've been a professional C, Perl, PHP and Python developer. I'm an ex-sysadmin from the late 20th century. These days I do more Javascript and CSS and whatnot, and promote UX and accessibility.
This is the first one of these I've tried and I'm a lazy person at best, so I did it in Vim:
let @q=':s/aA\|bB\|cC\|dD\|eE\|fF\|gG\|hH\|iI\|jJ\|kK\|lL\|mM\|nN\|oO\|pP\|qQ\|rR\|sS\|tT\|uU\|vV\|wW\|xX\|yY\|zZ\|Aa\|Bb\|Cc\|Dd\|Ee\|Ff\|Gg\|Hh\|Ii\|Jj\|Kk\|Ll\|Mm\|Nn\|Oo\|Pp\|Qq\|Rr\|Ss\|Tt\|Uu\|Vv\|Ww\|Xx\|Yy\|Zz//g @qq'
There are only 52 combinations after all. Takes about 2 minutes (with nohlsearch...)
I am an OpenEdge (aka Progress) developer that loves clean code and good looking applications that are easy to use. My main pet project is the Progress DataDigger
Wow, thanks for the idea to use a stack. I revised my first solution in OpenEdge 4GL and it went down from 39 second (yes, seconds) to 240 msec. A-ma-zing!
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
Dumb order of operations mistake got me to this point 🙃was missing the parens around the last half of the conditional since like 12:20. This mirrors the classic stack match parentheses problem.
My solution is kinda pretty though:
My first inclination was to do this recursively. Had to fudge it a bit because doing a truly recursive solution exceeds the max recursive depth :(
Here's my Python for part 1. It's very slow. Gonna look into refactoring to a regex or list comprehension solution for part 2. Kind of ready to move out of string manipulation land either way!
Wow, that stack thing is clever! It didn't even occur to me that you could evaluate as you go in a single pass through the polymer. Implementing the stack in c# (from the impl i posted below) went from
~2:50 => 268ms
:OCool! Yeah -- it's kind of a play on this classic problem, which is how I recognized it!
Very slick solution!
Maybe most already know... But, parsing the polymer string as a
string
and using str.replace twice as you do is still much faster than using polymer as a list and using a list-comprehension to remove units (35% slower).E.g.
Wow, I did not see the stack-based solution in there at all. I tried the recursive approach and then one similar to @thejessleigh and stopped there! At least I enjoyed this one more than Day 4!
I went with RegExp solution. I created a regexp for all the reactions and delete everyting, then iterate until no more matches
Time: 0.265s
For part b y reuse all the code but add this
Time: 3.693s
I find the part b is a little slow using this approach
this one was easy (to me!) in comparison to yesterdays, might even be the easiest thus far. All that being said, DAMNNNNNN is part 2 slow, at least with my current impl.
00:07 run time for part 1
02:50 run time for part 2 :OOOOO
Didn't think about the stack approach til seeing other's solutions, wow so much faster! Didn't really occur to me you could accomplish the whole reduction in a single pass through the polymer.
Part 1 went from 00:07 -> 12ms
Part 2 went from 02:50 -> 269ms
I wrote this solution pretty quickly... and then kept having the wrong value.
I went down a rabbit hole involving hexdump and such because I was pretty confident in what the code was supposed to do and didn't see a valid result come up. It drove me crazy, but finally after way more time digging than I'd like to admit, here's my solution:
I'll try and find some time to write up what problem I encountered
I reached for regular expressions in Perl. I wasn't sure about the performance, but 0.4s for the part 2 seemed enough.
The regex is constructed dynamically and simply lists all the possible pairs to remove.
For the part 2, I discovered you can start from the already reduced polymer, which improved the performance more than 10 times.
...welp, that was a solid "d'oh" moment, opening this thread. Hindsight is 20/20.
Here's the brute force way in F#. I'll have to come back to it after work to implement the smart way.
Yep, stack-based trick worked just fine:
Replacing
reactString
withreactQuickly
was enough to bring the total runtime from several (many) minutes to around 3 seconds.Using the tip from @choroba and reducing everything first would probably speed it up even more.
I originally had an iterative, while-loopy, pointer-based solution, and that ran OK, but like a lot of people, I realized that it's basically an extended version of the "Matching Parentheses" problem, and that a stack would let me iterate through the list more with less backward steps. Now it runs pretty quick!
Rust's lack of easy handling of upper- and lowercase characters kind of bummed me out.
Part 1
Part 2
For part 2, I didn't iterate over all 26 letters of the alphabet no matter what, I used a set to figure out what characters were in the input and only loop over those. In this case, it doesn't help, because the input has all 26 anyhow. Oh well. I learned a lot about
.collect()
today.PHP
Second part looks kind of long because I figured it would be faster to just declare the letters rather than generate the letters programmatically. I'm pretty sure there's a way to improve performance here, since this has a long-ish runtime, but I wasn't up to optimizing performance at midnight, haha. I might fiddle with this some more to see if I can do better.
Part 1:
Part 2:
I am using Advent of Code to learn Golang, and here is the solution I came up with. Suggestions for improvements are always welcome!
I was first using a list which would go from left to right and remove pairs as they were found reacting, shifting one step to the left and continue finding pairs that react. However, this could be simplified by using Ali Spittel's stack solution in Python.
With this inspiration, here is my Golang solution. I have added timing functions just to print out the running time as I have compared them to Python it is finally a speed up using Golang for the solution in this case!
Nice!
Kotlin Solution
A nice easy one! Just fold into a stack and bada-bing-bada-boom!
Part 1
I started with String and
String.last
. This got me the solution, but it turned out to be really slow. Here's my updated code using a Java LinkedList instead, which is multiple orders of magnitude faster.Part 2
This one seemed a little too simple at first, but my brute force implementation worked in about a 1500ms (still noticeably slow). After checking that my answer was correct, I did the stack optimization above which pulled me back down to 80ms or so.
Overall, this was a nice mid-week breather, and I'm happy to spend more of my thought cycles doing my OSCP lab work.
JavaScript solution
For this solution I used a stack. If the unit on top of the stack can react to the incoming unit, I just pop it out of the stack. Otherwise, I push it and move on.
reader.js
05-common.js
05a.js
05b.js
I'm a bit behind on these, but catching up this weekend! Day 5 is my favorite so far. 😁
I actually managed to complete Part A with an O(n2) algorithm, before realizing that would be too slow for part B...then I realized I could use a stack to implement an O(n) algorithm.
Part B is iterating through all letters in the alphabet, doing the string replace and then, doing the same reduction as Part A, while tracking the minimum reduced length. I feel like there's a better way to do that, but...never figured it out.
I logged my output and saw that, for my input,
v
reduced far better than anything else.My gut tells me there's some pattern in the input that would tell you which char to remove, and that way you only have to do the reduction once (as opposed to 26 times).
I looked at that, but when it wasn't definitively the letter that showed up the most, I didn't look very much further.
I bet you're right though, there's some "clustering factor" or something that would tip you off for which letter is most productive.
Probably won't get an explanatory blog post on this one today, and need to catch up to finish/submit yesterday's, but here's my Clojure solution for Day 5 (see gist.github.com/ballpointcarrot/7e...
Hey, here's my solution with ruby.
Nice and clean!
My day 5 solutions in Elixir. I'm actually pretty happy with how the code turned out this time, might be the cleanest of all days up until now.
The main idea of the implementation of part one is to put characters on a stack, and then compare the head of the stack with the next element to determine if the head and the new element should stay or go.
Part two is a wrapper around the first part, where the input is preprocessed (e.g. units are removed) before being fed to the implementation of the first part. The units to remove are based on unicode numbers.
Common:
Part one:
Part two:
Ugh, I hated this one, mainly because I took a completely wrong approach at first in an effort to be very fast. It was fast alright but could I get it to produce the right answer?!
Ali Spittel's stack solution is beautiful.
Late, but very happy with this one and had a lot of fun
Node.js
Full code: github.com/MattMorgis/Advent-Of-Co...
This is the first one of these I've tried and I'm a lazy person at best, so I did it in Vim:
There are only 52 combinations after all. Takes about 2 minutes (with
nohlsearch
...)Stage 2? Eh, my lunchbreak is really for lunch.
Wow, thanks for the idea to use a stack. I revised my first solution in OpenEdge 4GL and it went down from 39 second (yes, seconds) to 240 msec. A-ma-zing!