re: Daily Coding Puzzles - Nov 4th - Nov 9th VIEW POST

FULL DISCUSSION
 

Thursday

Scramblies (5 KYU):

Complete the function scramble(str1, str2) that returns true if a portion of str1 characters can be rearranged to match str2, otherwise returns false

CodeWars

 

Solved it awhile ago in C#.

Warning: Really ugly...

using System;
using System.Collections.Generic;
using System.Linq;


  public class Scramblies
  {
    public static bool Scramble(string s1, string s2)
    {
      Func<string, Dictionary<char, int>> toMap = s =>
        s.GroupBy(c => c)
        .Select(g => new { g.Key, Count = g.Count() })
        .ToDictionary(pair => pair.Key, pair => pair.Count);

      var map1 = toMap(s1);
      var map2 = toMap(s2);

      foreach (KeyValuePair<char, int> p2 in map2)
      {
        if (!map1.ContainsKey(p2.Key)) return false;

        if (map1[p2.Key] < p2.Value) return false;
      }

      return true;
    }
  }
 

F#

let scramble str1 str2 =
    let letterLookup = str1 |> Seq.countBy id |> Map.ofSeq
    let requiredCounts = str2 |> Seq.countBy id
    requiredCounts |> Seq.forall (fun (letter, requiredCount) ->
        match letterLookup |> Map.tryFind letter with
        | None -> false // letter missing
        | Some count -> requiredCount <= count
    )

Edit: I had previously posted a version that had ever-so-slightly more performance but was more code. I'm also including that version below since it solves the problem differently.

let scramble str1 str2 =
    let letterPool = str1 |> Seq.sort |> List.ofSeq
    let requiredLetters = str2 |> Seq.sort |> List.ofSeq
    let rec loop requiredLetters letterPool =
        match requiredLetters, letterPool with
        | [], _ -> true // found all
        | _ :: _, [] -> false // pool ran out
        | letter :: _, next :: pool when letter > next ->
            loop requiredLetters pool // skip next in pool
        | letter :: required, next :: pool when letter = next ->
            loop required pool
        | _, _ -> false // letter not available in the pool
    loop requiredLetters letterPool

Here are the tests (console).

    [
        "rkqodlw", "world", true
        "cedewaraaossoqqyt", "codewars", true
        "katas", "steak", false
    ]
    |> List.iter (fun (str1, str2, expected) ->
        let actual = scramble str1 str2
        let e = if expected = actual then "√" else "X"
        printfn "%s %A ==> %b, %b" e (str1, str2) expected actual
    )
    // √ ("rkqodlw", "world") ==> true, true
    // √ ("cedewaraaossoqqyt", "codewars") ==> true, true
    // √ ("katas", "steak") ==> false, false
 

Nice! yeah -- I only sometimes think about the efficiencies of these. They're contrived and we're doing them for fun. Part of me cares, part of me doesn't haha

I agree. I would have posted a much more expressive version, but you beat me to it! I did find an alternative way of solving the problem that was a little more expressive and nearly the same perf. I updated my post to include it.

 

Not a fancy solution, but it seems to work 😃

function scramble(s1, s2) {
    if (s1.length < s2.length) return false;

    const _s2 = s2.split("");

    s1.split("").forEach(val => {
        if (_s2.includes(val)) _s2.splice(_s2.indexOf(val), 1);
    });

    return _s2.length == 0;
}
 

Rust

fn scramble(str1: &str, str2: &str) -> bool {
    if str2.len() > str1.len() {
        return false;
    }
    str2.chars().all(|c| {
        str1.chars().filter(|&ch| ch == c).count() >= str2.chars().filter(|&c2| c2 == c).count()
    })
}
 

Go

func Scramble(str1 string, str2 string) (result bool) {
    m := make(map[string]int)

    for _, item := range strings.Split(str1, "") {
        m[item] += 1
    }

    for _, item := range strings.Split(str2, "") {
        if val, ok := m[item]; !ok || val == 0 {
            return false
        } else {
            m[item] -= 1
        }
    }

    return true
}
 

Python!

def scramble(s1,s2):
    for letter in set(s2):
        if s1.count(letter) < s2.count(letter):
            return False
    return True
 

Common Lisp


(defun scramble (source target)
          (let ((source (coerce source 'list))
                (target (coerce target 'list)))
            (cond ((null target) t) 
                  ((member (first target) source)
                   (scramble (remove (first target) source :count 1)
                             (rest target))))))

(mapcar #'(lambda (args) (apply #'scramble args))
        '(("rkqodlw" "world")
          ("cedewaraaossoqqyt" "codewars")
          ("katas" "steak")
          ("scriptjavx" "javascript")
          ("scriptingjava" "javascript")
          ("scriptsjava" "javascripts")
          ("javscripts" "javascript")
          ("aabbcamaomsccdd" "commas")
          ("commas" "commas")
          ("sammoc" "commas")))
;; => (T T NIL NIL T T NIL T T T)
 
function scramble(str1, str2) {
 let letterHolder = {};
 for (let letter of str1) {
   if(letterHolder[letter]) letterHolder[letter]++;
   else letterHolder[letter] = 1;
 }
 for (let letter of str2) {
   if(!letterHolder[letter]) return false
   else letterHolder[letter]--;
 }
 return true;
}
code of conduct - report abuse