Today I did few katas on codewars. One of the kata was this one.

Basically, you need to change every character in a word. If it appears multiple times, then change it to ), otherwise change it to (.

What I learned is simple solution using find() and rfind().

As usual, there are many approaches to solve this challenge, so let's see two of them first.

Solution 1: obvious

  • create a HashMap and count each character occurrence
  • iterate via characters again, and based on its occurrence, change the character

solution 2: small improvement

It is based on simple observation that we don't actually need to count occurrence of each character. We need to know if this character already appeared in a word:

  • use HashSet instead of HashMap
  • for each character check if it's already in a set
  • if it is, then we can already change the character to ), otherwise add it to the set
  • at the end change each of the remaining characters to ( because if those characters were not changed, that's because they were not present in the set, so those are not duplicated

the trick

Nice feature of codewars is that you can see others solutions. Moreover, you can filter them by "Clever" or by "Best practices". There was one clever solution which caused "oh!" effect.

The idea is similar to our previous solution - we don't need to count characters. But the way we determine if character is duplicated is smarter.

  • For each character, run word.find(character) to get index of the character in a word,
  • But we'll find the same character we are already checking
  • To fix the issue, we'll do word.rfind(character) - reverse search - and compare two found indices
  • If those indices are the same it means that there is only one such character
  • Otherwise there are more

The solution is simple and clean as following:

fn duplicate_encode(word: &str) -> String {
    let word = String::from(word).to_lowercase();
    word.chars()
        .map(|c| {
            if word.find(c) == word.rfind(c) {
                '('
            } else {
                ')'
            }
        })
        .collect()
}