loading...

Daily Challenge #244 - Search for Letters

thepracticaldev profile image dev.to staff ・1 min read

Create a function which accepts one arbitrary string as an argument, and return a string of length 26. The objective is to set each of the 26 characters of the output string to either '1' or '0' based on the fact whether the Nth letter of the alphabet is present in the input (independent of its case).

So if an 'a' or an 'A' appears anywhere in the input string (any number of times), set the first character of the output string to '1', otherwise to '0'. if 'b' or 'B' appears in the string, set the second character to '1', and so on for the rest of the alphabet.

For instance:
"a **& cZ" => "10100000000000000000000001"
'Abc e $$ z' => "11101000000000000000000001"

Tests

change("!!a$%&RgTT")
change("")
change("abcdefghijklmnopqrstuvwxyz")
change("aaaaaaaaaaa")

Good luck!


This challenge comes from Roy Gardiner on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Posted on by:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

Discussion

markdown guide
 

Rust

yay fun with bitsets

fn change(hay: &str) -> String {
    let mut output = 0u32;
    for c in hay.chars() {
        match c {
            'a'..='z' => output |= 1u32 << (b'z' - c as u8),
            'A'..='Z' => output |= 1u32 << (b'Z' - c as u8),
            _ => {}
        }
    }
    format!("{:026b}", output)
}

// "Test"

fn main() {
    assert_eq!(change("a **& cZ"), "10100000000000000000000001");
    assert_eq!(change("Abc e $$ z"), "11101000000000000000000001");

    let c = |x| println!(r#"{} <= "{}""#, change(x), x);
    // "Tests"
    c("!!a$%&RgTT");
    c("");
    c("abcdefghijklmnopqrstuvwxyz");
    c("aaaaaaaaaaa");
}

On infinitely long strings it would be good to:

  1. Shrink the range when high or low characters already matched
  2. Return early if we achieve 0b11111111111111111111111111

but on such short strings these would both be slower

Edit:

I have stolen the ascii case sensitivity bit from @miketalbot and will now proceed to pleasure myself with this fish

fn change(hay: &str) -> String {
    let mut output = 0u32;
    for c in hay.chars() {
        if let c @ b'A'..=b'Z' = c as u8 & 0b11011111 {
            output |= 1u32 << (b'Z' - c)
        }
    }
    format!("{:026b}", output)
}

Is this actually faster?
No idea.
Alternatively

if let c @ b'A'..=b'Z' | c @ b'a'..=b'z' = c as u8 {
    output |= 1u32 << (b'Z' - (c & 0b11011111))
}

or even just

if matches!(c, 'A'..='Z' | 'a'..='z') {
    output |= 1u32 << (b'Z' - (c as u8 & 0b11011111))
}

🤷

 

Here is a C++ solution,

string change(string s){
    string st(26, '0');

    for(char c : s)
        if(islower(c) || isupper(c))
            st[tolower(c)-'a'] = '1';

    return st;
}
 

Python 3.7

# solution
import string
change = lambda s: "".join(['1' if c in s.lower() else '0' for c in string.ascii_lowercase])

# testcases
for case in ["!!a$%&RgTT", "", "abcdefghijklmnopqrstuvwxyz", "aaaaaaaaaaa"]:
    print(f"change({case}) = {change(case)}")

Try it online!

(Sorry. I didn’t manage for format the code in markdown properly on my iphone)

 

I had roughly the same idea:

def isin(text):
  text = text.lower()
  return "".join([str(1)
                  if letter in text
                  else str(0)
                  for letter in "abcdefghhijklmnopqrstuvwxyz"])
 

This is a one statement in JS that works:

    (input)=>input.match(/[a-zA-Z]/g)
        .reduce((c,a)=>(c[(a.charCodeAt(0) & 31) -1]='1',c), 
           Array.from('0'.repeat(26)))
       .join('')
 

Here is a Ruby solution

def change(str)
  result = '0' * 26
  str = str.downcase
  str.each_char do |char|
    result[(char.ord - 97)] = '1' if char.ord.between?(97, 122)
  end
  result
end
 

Ruby can also multiply strings by numbers, which is a bit easier than joining an array:

'0' * 26
#=> "00000000000000000000000000"
 

Thanks Michael, I like that better.

 

Here is the Python solution:

def change(st):
    answer_string = [
        '0','0','0','0','0','0','0','0','0','0','0','0','0',
        '0','0','0','0','0','0','0','0','0','0','0','0','0',
    ]

    upper_alphabets = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

    for char in st:
        upper_index = 0
        for upper_alphabet in upper_alphabets:
            if upper_alphabet == char or upper_alphabet.lower() == char:
                answer_string[upper_index] = '1'
            upper_index += 1

    return "".join(answer_string)
 
const countEm = (str) => {
  str = str.toLowerCase();
  return 'abcdefghijklmnopqrstuvwxyz'.split('').map((letter) => {
    return str.indexOf(letter) === -1 ? '0' : '1';
  }).join('')
};

Thanks for the challenge!

 

There's also String.prototype.includes

const letters = "abcdefghijklmnopqrstuvwxyz".split("")
const countEm = str => {
  str = str.toLowerCase()
  return letters.map(letter => str.includes(letter) ? "0" : "1").join("")
}
 

C++17


#include<bits/stdc++.h>
using namespace std;
int main() {
    string str;
    vector<int>vec(26, 0);
    getline(cin, str);
    transform(str.begin(), str.end(), str.begin(), ::tolower);
    for (auto i = str.begin(); i != str.end(); ++i) {
        if (isalpha(*i)) {
            vec[*i - 'a'] = 1;
        }
    }
    for (auto i = vec.begin(); i != vec.end(); ++i) {
        cout << *i;
    }

}