DEV Community

Discussion on: Daily Coding Puzzles - Nov 4th - Nov 9th

Collapse
 
aspittel profile image
Ali Spittel

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

Collapse
 
dance2die profile image
Sung M. Kim

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;
    }
  }
Collapse
 
kspeakman profile image
Kasey Speakman • Edited

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
Collapse
 
aspittel profile image
Ali Spittel

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

Thread Thread
 
kspeakman profile image
Kasey Speakman • Edited

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.

Collapse
 
susickypavel profile image
Pavel Susicky

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;
}
Collapse
 
jay profile image
Jay

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()
    })
}
Collapse
 
thejessleigh profile image
jess unrein

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
}
Collapse
 
gypsydave5 profile image
David Wickes

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)
Collapse
 
clandau profile image
Courtney
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;
}
Collapse
 
aspittel profile image
Ali Spittel

Python!

def scramble(s1,s2):
    for letter in set(s2):
        if s1.count(letter) < s2.count(letter):
            return False
    return True
Collapse
 
danielhao5 profile image
Daniel Hao • Edited

python

def scramble (s1, s2):
    return not(Counter(s2) - Counter(s1))
Enter fullscreen mode Exit fullscreen mode
Collapse
 
andkushnir profile image
Andrey Kushnir

Python style
def scramble(s1, s2):
return all([s1.count(l) >= s2.count(l) for l in s2])