LeetCode 문제 중 난이도 EasyValid Anagram이다. 문제 링크는 포스팅의 맨 마지막에 추가하였다. 언어는 Java를 사용했다.



  문제해석

s와 t 두개의 문자열을 받아 t가 s의 애너그램(철자순서를 바꾼말)인지 확인하는 함수를 작성하라.

* 문자열은 소문자로만 이루어져있다고 가정한다.







첫번째 방법은 s와 t를 배열에 담아 정렬한 뒤 비교하는 방법이다. 문자열의 길이가 다르다면 당연히 같을수 없으므로 false를 리턴한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
    public boolean isAnagram(String s, String t) {
 
        //두 문자열의 길이가 다르면 false
        if (s.length() != t.length()) return false;
        
        //s와 t를 각각 char배열로 변환
        char[] str1 = s.toCharArray();
        char[] str2 = t.toCharArray();
        
        //정렬한다
        Arrays.sort(str1);
        Arrays.sort(str2);
        
        //철자가 같다면 정렬후 두 배열값이 같아야함
        return Arrays.equals(str1, str2);
        
    }
}
cs

16번째줄의 Arrays.equals(array1,array2)는 배열 안의 값이 같은지 확인하는 메소드로 array1.equals(array2)와는 다르다. 후자는 array1==array2와 같은의미로 실제로 두 배열이 주소값까지 같은 동일한 배열인지 확인하는 메소드이다.





두번째 풀이는 각 스펠링이 나온 개수를 배열에 저장하고 비교하는 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
    public boolean isAnagram(String s, String t) {
        
        if (s.length() != t.length()) return false;
        
 
        //a부터 z까지 각각의 알파벳이 나온 횟수를 담기위한 배열 생성
        int[] table = new int[26];
        
        //s의 스펠링들이 각각 몇번 나왔는지 1씩 증가시키면서 table에 저장
        for (int i = 0; i < s.length(); i++) {
            table[s.charAt(i) - 'a']++;
        }
        
        //table에서 t에도 있는애들을 하나씩 감소시킨다
        for (int i = 0; i < t.length(); i++) {
            table[t.charAt(i) - 'a']--;
            if (table[t.charAt(i) - 'a'< 0) {
                return false;
            }
        }
        
        return true;
    }
}
cs


먼저 a부터 z까지 26개의 알파벳이 나온 횟수를 담기위한 배열을 만든다. 그러고나서 s를 탐색하면서 각 알파벳이 나올때마다 해당 위치의 배열값을 1씩 증가시킨다. 그 다음에 t를 탐색하면서 해당 위치의 배열값을 1씩 감소시킨다. 전체 배열이 0으로 채워지면 true를 리턴한다.


12,17,18번째 줄의 chatAt(i) - 'a'는 알파벳이 아스키코드(0~127)의 값으로 저장되는 것을 이용한 것이다. a~z까지의 알파벳에서 a를 빼주면 0~25까지의 숫자가 나온다.




아스키값 참고



문제 출처 - https://leetcode.com/problems/valid-anagram

자바 정규표현식(Regular expression)에 대해 알아보자.




  정규표현식이란?

Regular expression, 줄여서 Regex라고도 부른다. 특정한 패턴을 가진 문자열(String)을 표현하는데 사용하는 형식언어이다. 정규표현식을 이용하여 특정 패턴에 맞는 문자열을 찾아 바꾸거나, input값이 유효한지 체크하는 데에 주로 사용한다.



예를 들어 핸드폰번호로 010-3e88-2pe7과 같은 입력값은 말이 되지 않는다. 정규표현식을 이용해 입력값의 패턴이 유효한 패턴인지 validation이 가능하다.






자 그럼 자바에서는 정규표현식을 어떤식으로 사용할까?



s.matches("regex") : s가 regex 패턴과 일치하는지 확인

s.replaceFirst("regex","replacement") : 가장 먼저나온 regex를 replacement로 치환

s.replaceAll("regex","replacement") : regex와 일치하는 부분을 전부 replacement로 치환

s.split("regex") : regex를 기준으로 s를 잘라 배열에 집어넣음




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class HelloWorld {
 
    public static void main(String[] args) {
        
        String s1 = "Becca is genius";
        String s2 = "becca.kim@gmail.com";
        
        if(s1.matches("(?i)b\\D+$")){
            System.out.println("(?i)b\\D+$ 정규표현식 일치");
        }
        
        if(s1.matches("b\\D+$")){
            System.out.println("b\\D+$ 정규표현식 일치");
        }
                
        if(s2.matches("[a-z0-9\\.]+@[a-z0-9]+\\.[a-z]+")){
            System.out.println("정상적인 이메일 주소");
        }
        
        s1=s1.replaceFirst("[c]+""k");
        System.out.println(s1);
        
    }
}
cs

8번째 줄의 (?i)b\\D+$는 대소문자 구분없이 b로 시작하고 숫자가 아닌 문자들이 반복되다가 끝나는 문자열을 말한다. 만약에 s1 중간에 숫자가 하나라도 있으면 s1.matches("(?i)b\\D+$")는 false가 된다.


12번째 줄의 b\\D+$는 s1의 첫문자가 대문자이기 때문에 출력되지 않는다.


16번째줄의 [a-z0-9\\.]+@[a-z0-9]+\\.[a-z]+는 알파벳,숫자,마침표문자로 이루어진 아이디@알파벳들.알파벳들 패턴을 나타낸다.

* 위의 패턴이 우리가 사용하는 실제 이메일 주소 정규표현식은 아니다. 어떤 사이트는 이메일이 주소에 마침표를 허용하지 않을 수도 있고 _등의 특수문자도 더 사용 가능한 경우도 있다.



코드를 돌려보면 아래와 같은 결과가 나온다. 


정규표현식을 처음 접하면 조금 복잡해 보인다고 생각할 수도 있지만 몇번 직접 해보면 이해가 빠를 것이다. 비밀번호패턴, 주소, 핸드폰번호 등 여러가지 정규표현식을 아래의 사이트에서 연습해보는것을 추천한다.

http://regex101.com

 

LeetCode 문제 중 난이도 Easy인 Hamming Distance이다. 문제 링크는 포스팅의 맨 마지막에 추가하였다. 언어는 Java를 사용했다.

 

  문제해석
두 정수 사이의 해밍거리는 상응하는 위치에서 값이 다른 비트의 개수이다.
두 정수 x,y를 입력으로 받아 해밍거리를 구하여라.

* x,y는 0 이상 2^31 이하

 

 

 

 

비트연산자를 이용하면 서로 다른 비트만 1로 표시할 수 있다. 

 

0101 ^ 0100 = 0001

 

100111 ^ 101011 = 001100

 

 

비트연산자란?

비트는 컴퓨터에서 데이터를 나타내는 최소 단위이다. 모든 데이터는 0과 1의 조합으로 구성되는데, 이 0또는 1이 하나의 비트이다. 1개의 비트는 두 가지 상태를 나타낼 수 있으므로 n개의 비트로는 2ⁿ가지의..

beccacatcheserrors.tistory.com

 

 

입력 받은 x와 y에 XOR 연산을 하면 x와 y에서 비트값이 다른 위치의 비트를 1로 바꾸어다. 그 결과값에서 1의 개수를 리턴하면 된다. 자바에서 제공해주는 Integer.bitCount를 사용하면 1줄만에 해결이 가능하다.

1
2
3
4
5
public class Solution {
    public int hammingDistance(int x, int y) {
        return Integer.bitCount(x^y);
    }
}
cs

 

 

하지만 왠지 이 방법은 너무 치사한(?)것 같으니 비트의 개수를 직접 세어 주자.

 

 

 

 

어떤 숫자에서 1을 빼면 마지막 1이 있는 위치부터 끝까지 비트가 바뀐다

 

1001100 - 1 = 1001011

 

그 상태에서 원래의 숫자와 AND(&)연산을 하면 맨 오른쪽의 1이 사라지게 된다.

 

1001100 & 1001011 = 1001000

 

 

 

 

위의 원리를 이용해 코드에 적용해 보자. x^y의 결과값이 0이 될 때까지 오른쪽에서부터 1을 차례로 없애주고 count를 1씩 늘린다. 마지막에는 count 값을 리턴한다.

1
2
3
4
5
6
7
8
9
10
11
class Solution {
    public int hammingDistance(int x, int y) {
        int xor=x^y, count=0;
        
        while(xor!=0) {
            xor=xor&(xor-1);
            count++;
        }
        return count;
    }
}
cs

연산 횟수의 최댓값은 Integer의 비트수로 상수로 정해져 있으니 시간복잡도는 O(1)이고 공간복잡도도 O(1)이다.

 

+ Recent posts