b0891edd652eaddeb0a401dd7d7498cbba272886 — William Vaughn 1 year, 4 months ago 3451ba0
add codewars recover a secret string from random triplets
A codewars/src/clj/org/willvaughn/codewars/random_triplets.clj => codewars/src/clj/org/willvaughn/codewars/random_triplets.clj +77 -0
@@ 0,0 1,77 @@
(ns org.willvaughn.codewars.random-triplets
  (:require [clojure.string :as s]
            [clojure.set :as set]))

;;; There is a secret string which is unknown to you. Given a collection of random triplets from the string, recover the original string.
;;; A triplet here is defined as a sequence of three letters such that each letter occurs somewhere before the next in the given string. "whi" is a triplet for the string "whatisup".
;;; As a simplification, you may assume that no letter occurs more than once in the secret string.
;;; You can assume nothing about the triplets given to you other than that they are valid triplets and that they contain sufficient information to deduce the original string. In particular, this means that the secret string will never contain letters that do not occur in one of the triplets given to you.

;;; For each distinct letter
;;; If it only shows up as a first letter in both lists, that's the next letter
;;; move the triplets that start with that letter to the right list.
;;; Strip the first character off the triplets (or remainders) that start with target.
;;; For the test example this is what happens to triplets, remains, secret at each iteration.
;;; ["tup" "whi" "tsu" "ats" "hap "tis" "whs"] [] []
;;; ["tup" "tsu" "ats" "hap" "tis"] ["hi" "hs"] [\w]
;;; ["tup" "tsu" "ats" "tis"] ["i" "s" "ap"] [\w \h]
;;; ["tup" "tsu" "tis"] ["i" "s" "p" "ts"] [\w \h \a]
;;; [] ["i" "s" "p" "s" "up" "su" "is"] [\w \h \a \t]
;;; [] ["s" "p" "s" "up" "su" "s"] [\w \h \a \t \i]
;;; [] ["p" "up" "u"] [\w \h \a \t \i \s]
;;; [] ["p" "p"] [\w \h \a \t \i \s \u]
;;; [] [] [\w \h \a \t \i \s \u \p]

(defn next-letter? [triplets remains target-letter]
  (let [index-of-letter (fn [triplet]
                          (s/index-of triplet target-letter))
        grouped-triplets (group-by index-of-letter triplets)
        grouped-remains (group-by index-of-letter remains)]
    (when (every? #(or (empty? %)
                       (set/superset? #{nil 0} (set (keys %))))
                  [grouped-triplets grouped-remains])
      (let [{next-triplets nil triplet-remains 0} grouped-triplets
            {remains-missed nil remains-hit 0} grouped-remains
            next-remains (into (vec remains-missed)
                                 (map #(subs % 1) (concat triplet-remains remains-hit)))]
        {:next-triplets next-triplets
         :next-remains next-remains
         :target-letter target-letter}))))

(defn deduce-secret
  ([letters triplets] (deduce-secret letters triplets [] []))
  ([letters triplets remains] (deduce-secret letters triplets remains []))
  ([letters triplets remains secret-chars]
   (let [{:keys [next-triplets
                 target-letter]} (some (partial next-letter? triplets remains) letters)]
     (if (and (empty? next-triplets)
              (empty? next-remains))
       (apply str secret-chars)
       (recur (remove #(= target-letter %) letters)
              (or next-triplets [])
              (or next-remains [])
              (conj secret-chars target-letter))))))

(defn recover-secret [triplets]
  (deduce-secret (distinct (apply concat triplets))

;;; There's some interesting solutions.
;;; I like this one that does what I did but much more intelligently.
;;; When I do this again, I should seek to understand their approach with sets here.
;;; (defn recover-secret [triplets]
;;;   (let [order (->> triplets
;;;                    (map (fn [[a b c]] {c #{a b} b #{a} a #{}}))
;;;                    (apply merge-with union))]
;;;     (loop [seen #{}
;;;            done []
;;;            order order]
;;;       (if-let [c (ffirst (filter (fn [[k v]] (subset? v seen)) order))]
;;;         (recur (conj seen c) (conj done c) (dissoc order c))
;;;         (apply str done)))))

A codewars/test/clj/org/willvaughn/codewars/random_triplets_test.clj => codewars/test/clj/org/willvaughn/codewars/random_triplets_test.clj +6 -0
@@ 0,0 1,6 @@
(ns org.willvaughn.codewars.random-triplets-test
  (:require [clojure.test :refer :all]
            [org.willvaughn.codewars.random-triplets :refer (recover-secret)]))

(deftest solve
  (is (= (recover-secret ["tup" "whi" "tsu" "ats" "hap" "tis" "whs"]) "whatisup")))