Password Cracker

Problem is described here: https://www.hackerrank.com/challenges/password-cracker. First, this is in the recursion section of the page - that should give you a hint that a recursive solution might be your best bet.
The problem can be solved by:
 - Determining whether the attempt password starts with one of the passwords
 - If so, remove it from the attempt password, call it recursively
 - Keep track of the passwords used
 - The moment that the attempt password is empty, you have found a solution
 - If none is found, well, none is found

My first attempt kind of worked, but timed out on few cases:


When looking at the test cases that timed out, one stood out:

a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaa aaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab

Now if you notice it, it is clear that the attempt password will never have a solution given that there is one character in the attempt password that is not present in any of the passwords. Adding this small check to the code was enough to make it pass all through. Code is below, cheers!!! Marcelo.



using System; using System.Collections.Generic; using System.IO; using System.Collections; class Solution { static void Main(String[] args) { int t = Int32.Parse(Console.ReadLine()); string[] solution = new string[t]; for (int i = 0; i < t; i++) { int n = Int32.Parse(Console.ReadLine()); string[] words = Console.ReadLine().Split(' '); string attempt = Console.ReadLine(); string[] wordsInPass = new string[3000]; for (int j = 0; j < wordsInPass.Length; j++) wordsInPass[j] = ""; Hashtable mark = new Hashtable(); foreach (string w in words) { foreach (char c in w) { if (!mark.ContainsKey(c)) mark.Add(c, true); } } bool found = true; foreach (char c in attempt) { if (!mark.ContainsKey(c)) { found = false; break; } } if (!found) { solution[i] = "WRONG PASSWORD"; } else { if (Process(attempt, words, n, wordsInPass, 0)) { solution[i] = ""; foreach (string s in wordsInPass) { if (s.Length > 0) { solution[i] += s + " "; } } } else { solution[i] = "WRONG PASSWORD"; } } } for (int i = 0; i < t; i++) { Console.WriteLine(solution[i]); } } static bool Process(string attempt, string[] words, int n, string[] wordsInPass, int currentIndex) { if (attempt.Length == 0) { return true; } for (int i = 0; i < n; i++) { if (attempt.StartsWith(words[i])) { wordsInPass[currentIndex] = words[i]; if (Process(attempt.Substring(words[i].Length), words, n, wordsInPass, currentIndex + 1)) { return true; } wordsInPass[currentIndex] = ""; } } return false; } }

Comments

  1. Hm, interesting solution Marcelo! Somehow when I read this problem I immediately thought about a classical dynamic programming problem of checking whether a string can be split into words belonging to a given some dictionary. Here is my DP solution http://ideone.com/GWJncr

    ReplyDelete
  2. Ah, so awesome!!! As you know my DP skill is still pretty dormant in me, it doesn't come naturally :) Amazing that your code runs in O(#attempt^2), super neat!!!

    ReplyDelete
    Replies
    1. you're being unfair to your DP skills - I'm just trying to follow your lead ;)

      Delete
    2. :-) no man, trust me, it only comes to me with way too much effort

      Delete
    3. based on the number of problems you've solved using DP I cannot accept your statement :P

      Delete

Post a Comment

Popular posts from this blog

Advent of Code - Day 6, 2024: BFS and FSM

Golang vs. C#: performance characteristics (simple case study)

Advent of Code - Day 7, 2024: Backtracking and Eval