Counting Duplicates<6 kyu>

jjanmo·2020년 1월 24일
0

Codewars에서 뒹굴기

목록 보기
22/32

문제링크

문제

Count the number of Duplicates
Write a function that will return the count of distinct case-insensitive alphabetic characters and numeric digits that occur more than once in the input string. The input string can be assumed to contain only alphabets (both uppercase and lowercase) and numeric digits.

Example

"abcde" -> 0 # no characters repeats more than once
"aabbcde" -> 2 # 'a' and 'b'
"aabBcde" -> 2 # 'a' occurs twice and 'b' twice (`b` and `B`)
"indivisibility" -> 1 # 'i' occurs six times
"Indivisibilities" -> 2 # 'i' occurs seven times and 's' occurs twice
"aA11" -> 2 # 'a' and '1'
"ABBA" -> 2 # 'A' and 'B' each occur twice

🚩 문제해석
대소문자 구별 없이 알파벳과 숫자로 이루어진 문자열에서 2번이상씩 나온 글자의 개수를 리턴하시오.

case-insensitive : 대소문자 구별없이
대소문자를 구별한다 = sensitive 하다 ↔ 대소문자를 구별하지않는다 = insensitive 하다

문제 접근

이 문제를 두가지로 풀었다.

  • 첫번째 풀이
    사실 이 풀이는 별로 마음에 들지 않았다. 왜냐하면 여러가지로 고민 끝에 다 실패하고 마지막으로 궁여지책으로 짜낸 풀이이다. 물론 이 풀이가 잘못된 것은 아니니까...😓
    나의 생각을 적어본다.

    1. 문자열을 배열로 바꾸어 정렬한다.

    2. 정렬을 하게 되면 같은 것끼리 뭉치기된다.

    3. 덩어리(같은 것끼리 뭉친 부분배열)와 덩어리 사이로 넘어갈 때가 초점을 맞춘다.

    4. 만약 덩어리(부분배열)의 개수가 2개 이상이라면 구하고자 하는 조건에 맞는 것! 카운팅 하면 된다.

        function duplicateCount(text){
          const arr = text.toLowerCase().split('').sort();
          let compare = arr[0];
          let result = 0,  sameCnt = 1;
          for(let i = 1; i < arr.length+1; i++){
            if(compare === arr[i]) sameCnt++;
            else{
              if(sameCnt > 1) {
                  result++;
                  sameCnt = 1;
              }
              compare = arr[i];
            }
          }//for
          return result;
        }
  • 두번째 풀이
    이 풀이는 갑자기 생각이 난 풀이이다. 문자열의 구성이 정해져있다는 점에 초점을 맞추면, 특정 형식을 만들어서 접근하였다.

    1. 알파벳과 숫자 프로퍼티로 구성된 객체를 생성한다.

    2. 각 글자에 맞는 프로퍼티를 카운팅한다. 이 때 문자열 1회 순회!

    3. 객체 프로퍼티 중에 값이 1 초과인 것의 개수를 구한다.

         function duplicateCount(text){
           const format = {
             a : 0, b : 0, c : 0, d : 0, e : 0, f : 0,
             g : 0, h : 0, i : 0, j : 0, k : 0, l : 0,
             m : 0, n : 0, o : 0, p : 0, q : 0, r : 0,
             s : 0, t : 0, u : 0, v : 0, w : 0, x : 0,
             y : 0, z : 0, 1 : 0, 2 : 0, 3 : 0, 4 : 0, 
             5 : 0, 6 : 0, 7 : 0, 8 : 0, 9 : 0, 0 : 0
           }
      
           for(let i = 0; i < text.length; i++){
             format[text[i].toLowerCase()]++;
           }
           let cnt = 0;
           for(let key in format){
             if(format[key] > 1) cnt++
           }
           return cnt;
         }

## Best Solution
이번 문제는 여러 각도로 생각을 해봤는데 모두 실패했는데, 그런 생각을 담은 풀이가 모두 존재했다. 정말 다양한 생각은 끝이 없는 것 같다. 대단하다.
- 첫번째 풀이
```javascript
    function duplicateCount(text){
      return (text.toLowerCase().split('').sort().join('').match(/([^])\1+/g) || []).length;
    }

이 풀이는 한마디로 정리할 수 있다, 정규표현식을 배우자!

/([^])\1+/g
  • / : 구분자, /와/ 사이에 있는 의미를 이해하자

  • ( ) : 패턴 및 그룹화 = 첫번째 캡쳐링 그룹이라고 하는듯... ([^]) 1st Capturing Group

  • [^] : 모든 문자(글자)를 의미

  • \1+ : 주어진 문자열에서 첫번째 그룹핑([^])된 글자가 1번 초과(?)하여 나온 것들을 찾음

    여러가지 참고자료와 외국인 친구들의 이야기들 해석해보면 이러한듯... 이 부분에 있어서는 좀 더 확인이 필요하다. 😭

    결론은 위 정규표현식은 어떠한 문자라도 2번이상 나오는 문자를 뽑아내는 것을 의미한다.

  • 두번째 풀이

1    function duplicateCount(text){
2      return text.toLowerCase().split('').filter(function(val, i, arr){
3        return arr.indexOf(val) !== i && arr.lastIndexOf(val) === i;
4      }).length;
5    }

배열을 순회할 때 주어진 인덱스값(i)을 기준으로 앞쪽에 특정문자(val)가 더 존재하는지를 indexOf()를 통해서 확인하는 풀이이다. 만약에 3번라인의 코드를 이렇게 바꿔도 같은 결과가 나온다.

return arr.indexOf(val) === i && arr.lastIndexOf(val) !== i;

위 코드는 뒤쪽에 특정문자가 더 존재하는지를 확인하는 코드이다. 결국 같은 의미를 나타내는 것이다. 그런데 여기서 궁금한 점이 생겼다. 내가 사용한 indexOf()의 코드는 이렇다.

arr.indexOf(val) !== arr.lastIndexOf(val)

이 코드와는 무엇이 다른가? 이 코드는 메소드의 결과값으로 나온 값이 다르기만 하면 무조건 카운팅한다. 예를 들어, abcae 라고 했을 때, 위 문자열을 순회하면 앞 쪽에 a일 때도 카운팅하고 뒷 쪽에 a도 카운팅한다. 즉 중복이 발생하는 것이다. 이를 방지하기 위해서 arr.lastIndexOf(val) === i을 사용하여 가장 마지막에 나온 특정문자를 확인하여 이 때만 카운팅 되도록 구현하였다. 중복에 대한 문제는 시도때도 없이 불쑥 찾아오는 불청객이기에...😣 항상 생각을 하고 있어야만 하겠다.

  • 세번째 풀이
    function duplicateCount(text){
      var lower = text.toLowerCase();
      var count = 0;
      var used = [];

      lower.split('').forEach(function(letter) {
        if (!used.includes(letter) && (lower.split(letter).length - 1) > 1) {
          count++;
          used.push(letter);
        }
      });
      return count;
    }	

이 풀이는 접근이 기발한 풀이라고 생각한다. 어떤 글자를 기준으로 쪼갯을 때, 쪼갠 개수에 따라서 그 글자가 몇 개인지를 확인하여 중복된 글자를 체크하는 풀이이다.

결론

가볍게 해결할거라 생각한 문제를 통해 많은 것을 배웠다. 첫번째는 정규표현식에 대해서 또 알게 되었다. 하나씩 찾아보면서 마치 영어로 문장을 해석하는듯 했다. 정규표현식에 대한 설명, 개념같은 것들은 자료가 많이 있지만, 그것을 바탕으로 실질적인 문제에 적용하는 것은 또 다른 문제인 것 같다. 시행착오를 통해서 익혀가야할 문제인 것 같다. 두번째는 생각을 구현하는 여러가지 방법을 배웠다. 풀이를 보면서 나의 생각이 어느 부분에서 막혔는지를 알 수 있는 시간이였다.

🚀 문제를 풀어나갈 때 생각의 흐름을 정리합니다. 또한 새로운 풀이에 대한 코드를 분석하고 모르는 부분에 대해서 정리합니다. 생각이 다른 부분에 대한 피드백은 언제나 환영합니다. 틀린 내용에 대한 피드백 또한 항상 감사합니다.

profile
눈길을 걸어갈 때 어지럽게 걷지 말기를.

0개의 댓글