조건:

  • 1 <= words.length <= 20
  • 1 <= words[i].length <= 100
  • words의 각 원소는 대문자와 소문자의 혼용으로 이루어져 있습니다.

의역: 문자열들의 배열 words가 주어질 때 해당 문자열에서 미국식 키보드 기분 한 행의 키만 사용해서 만든 문자열들을 배열로 만들어 반환해라. 

 

미국식 키보드:

  • 첫 번째 행: "qwertyuiop",
  • 두 번째 행: "asdfghjkl", 
  • 세 번째 행: "zxcvbnm".

의역: 첫 번째 예시에서는 Alaska와 Dad 만이 2번째 행의 문자들로 구성되어 있기에 저 둘이 반환되었으며

두 번째 예시에서 omk는 첫 번째 두 번째 세 번째 모든 행의 문자들로 구성되어 있기에 빈 공간을 반환합니다.

세 번쨰 예시에서는 words의 원소 모두 2번째 행의 문자들로 구성되어 있기에 words 그대로를 반환합니다.

 

풀이: 

1. 정규식 이용

class Solution {
    // 시간 복잡도O(n*String.matches()) 공간복잡도 O(1)
    public String[] findWords(String[] words) {
        List<String> ans = new ArrayList<String>();
        // [a|b]* 는 a 또는 b가 0번 이상 반복되어 구성된 문자열임을 의미한다.
        for(int i=0;i<words.length;i++){
            if(words[i].matches("[q|Q|w|W|e|E|r|R|t|T|y|Y|u|U|i|I|o|O|p|P|]*")
            ||words[i].matches("[a|A|s|S|d|D|f|F|g|G|h|H|j|J|k|K|l|L]*")
            ||words[i].matches("[z|Z|x|X|c|C|v|V|b|B|n|N|m|M]*"))
                ans.add(words[i]);
        }
        return ans.toArray(new String[ans.size()]);
    }

}

1-1. (한 줄로 줄인 코드)

toLowerCase()를 통해 대소문자 구별을 없애고  | 조건을 추가하여 한 줄로 정리된 모습 

Stream.of(array): 지정된 array를 바탕으로 Stream 객체를 만듭니다.

Stream.filter(람다식): 람다식의 조건을 바탕으로 Stream 객체의 정보를 정리합니다.

String.mathch(정규식): 해당 정규식과 동일한 문자열이면 true 아니면 false를 리턴 합니다.

Stream.toArray(Type[]::new)는 Stream을 Type의 배열로 변환합니다.

public String[] findWords(String[] words) {
    return Stream.of(words).filter(s -> s.toLowerCase().matches("[qwertyuiop]*|[asdfghjkl]*|[zxcvbnm]*")).toArray(String[]::new);
}

2. Set 자료구조 활용 (브루트 포스)

class Solution {
        public String[] findWords(String[] words){
        ArrayList<String>list=new ArrayList<>();

        // mask로 사용할 set 초기화
        String p="qwertyuiop";
        HashSet<Character>a=new HashSet<>();
        for(int i=0;i<p.length();i++){
            a.add(p.charAt(i));
        }
        String q="asdfghjkl";
        HashSet<Character>b=new HashSet<>();
        for(int i=0;i<q.length();i++){
            b.add(q.charAt(i));
        }
        String r="zxcvbnm";
        HashSet<Character>c=new HashSet<>();
        for(int i=0;i<r.length();i++){
            c.add(r.charAt(i));
        }
        
        int j=0;
        for(int i=0;i<words.length;i++){
            String words2=words[i].toLowerCase();
            if(a.contains(words2.charAt(0))){
                for(j=1;j<words2.length();j++){
                    if(!a.contains(words2.charAt(j))){
                        break;
                    }
                }
                if(j==words[i].length()){
                    list.add(words[i]);
                }
            }
            else  if(b.contains(words2.charAt(0))){
                for(j=1;j<words2.length();j++){
                    if(!b.contains(words2.charAt(j))){
                        break;
                    }
                }
                if(j==words2.length()){
                    list.add(words[i]);
                }
            }
            else if(c.contains(words2.charAt(0))){
                for( j=1;j<words2.length();j++){
                    if(!c.contains(words2.charAt(j))){
                        break;
                    }
                }
                if(j==words2.length()){
                    list.add(words[i]);
                }
            }
        }
        return list.toArray(new String[list.size()]);
    }
}

메모: 더 간결하게 줄인다고 해당 코드가 무조건 효율적이지는 않다.

 

링크: https://leetcode.com/problems/keyboard-row/

조건:

  • 1 <= nums1.length <= nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 10^4
  • All integers in nums1 and nums2 are unique. (배열 안의 정수 값은 중복이 존재하지 않는다)
  • All the integers of nums1 also appear in nums2.

권장 사항: Could you find an O(nums1.length + nums2.length) solution? 

 

의역: 두 개의 정수 배열 nums2[]와 nums2[]의 부분집합 nums1[]이 주어질 때

ex: nums2 = {1,2,3,4,5,6}, nums1 = {1,5}

정수형 배열 ans[]를 반환하라 (nums1.length == ans.length)

ans[]의 각 인덱스값은 nums1[]의 각 인덱스 값과 매칭되어 ans[i] = function(nums[i]);

nums1의 각 인덱스의 해당하는 값을 nums2에서 찾고 그 뒤로 해당 값보다 큰 값이 나오면 그 값을 ans에 집어 넣는다.

만약 nums2에 뒤에 더 큰 값이 나오지 않는다면 -1을 넣는다.

 

의역: nums1[0]의 값은 nums2[2]의 값과 매칭되며 nums2 배열에서 다음 인덱스 안에 4보다 큰 값이 없으므로 ans[0] = -1

nums[1]의 값은 nums2[0]의 값과 매칭되며 nums2 배열에서 다음 인덱스 값이 1보다 큰 3이므로 ans[1] = 3

nums[2]의 값은 nums2[3]의 값과 매칭되며 nums2 배열에서 3 다음 인덱스가 존재 하지 않으므로 ans[2] = -1

 

의역: nums1[0]의 값은 nums2[1]의 값과 매칭되며 nums2 배열에서 다음 인덱스 값이 2보다 큰 3이므로 ans[0] = 3

nums[1]의 값은 nums2[3]의 값과 매칭되며 nums2 배열에서 3 다음 인덱스가 존재 하지 않으므로 ans[1] = -1

 

풀이: 

1. 정공법 브루트 포스

class Solution {
    // 시간 복잡도 O(n*m) 공간 복잡도 O(n)
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int ans[] = new int[nums1.length];
        Arrays.fill(ans,-1);
        for(int i=0;i< nums1.length;i++){
            int pos = -1;
            for(int j=0;j< nums2.length;j++){
                if(nums1[i]==nums2[j])
                    pos = j;
                if(pos!=-1&&nums2[pos]<nums2[j]){
                    ans[i] = nums2[j];
                    break;
                }
            }
        }
        return ans;
    }
}

2. 기수 정렬 응용 (정수형의 범위 만큼의 저장공간 생성 후 활용)

class Solution {
    // 시간 복잡도 O(n+m) 공간 복잡도 O(n)
    // 가능한 이유 nums1[i], nums2[i] 값의 범위가 좁기 때문에
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        // 0 <= nums1[i], nums2[i] <= 10^4
        int numsNextBigger[] = new int[10001];
        Arrays.fill(numsNextBigger,-1);
        int ans[] = new int[nums1.length];
        // numsBigerNext에 nums2의 모든 값에 대한 다음 큰 값의 정보를 담는다.
        for(int i=0;i<nums2.length;i++){
            for(int j=i;j<nums2.length;j++){
                if(nums2[i]<nums2[j]){
                    numsNextBigger[nums2[i]] = nums2[j];
                    break;
                }
            }
        }
        // 반복문을 통한 저장된 자료값 매핑
        for(int i=0;i<nums1.length;i++){
            ans[i] = numsNextBigger[nums1[i]];
        }
        return ans;
    }
}

3. monotone stack기법 변형

(스택의 원소들을 오름차순, 혹은 내림차순 상태를 유지하도록 하는 것)

스택에 숫자 x를 넣는다고 할 때 x를 넣기 전에 x 이상의 수를 모두 제거하고 x를 넣는 것.

class Solution {
    // 시간 복잡도 O(n) 공간 복잡도 O(n)
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int ans[] = new int[nums1.length];
        Stack<Integer> stack = new Stack<Integer>();
        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
        // nums2의 모든 값들의 nextGreaterElement 정보를 map에 저장
        for(int i=0;i<nums2.length;i++){
            int tmp = nums2[i];
            // stack에 순서대로 값을 push 하다가 다음 값이 전에 push 한 값보다 크다면
            while(!stack.isEmpty()&&tmp>stack.peek()) {
                // 전에 넣어둔 값을 제거 하고 전에 넣어둔 값의 nextGreaterElement로 map에 저장
                map.put(stack.pop(), tmp);
                // while문으로 반복하게 되어 차례대로 과정 수행
            }
            stack.push(tmp);
        }

        int i=0;
        for (int num:nums1) {
        // getOrDefault() 함수는 찾는 key 값이 없다면 정해둔 (-1)값을 반환  
            ans[i++] = map.getOrDefault(num,-1);
        }
        return ans;
    }
}

 

메모: 컬렉션의 빌트인 함수들을 사용하여 빅오 표시법의 시간복잡도를 줄여도 해당 빌트인 함수들의 소요 시간으로 인해 분명히 단순 빅오 표기법 상으로는 3번 풀이가 더 효율적이나 예상외로 2번이 더 효율적이었다.

되도록이면 빌트인 함수들을 사용하는 걸 줄이자.  

 

 

의역: 우리팀의 (리그오브레전드)티모 챔피언이 적의 애쉬 챔피언을 공격했을 때 티모의 독 데미지는 정수 duration 초 만큼 지속되며 각 초마다 1초의 데미지를 받습니다.

만약 독 데미지의 0이 되기전에 다시 티모가 공격을 한다면 데미지를 받는 시간이 duration으로 초기화 됩니다. timeSeries[] 배열이 티모가 공격을 한 시간일 때 에쉬가 받게 되는 총 데미지를 반환하시오

의역: 1초에 티모의 공격을 받는다면 - 1초: 총 데미지 1, 2초: 총 데미지 2 끝

4초에 티모의 공격 - 4초: 총 데미지 3, 5초: 총 데미지 4 끝

의역: 1초에 티모의 공격을 받는다면 - 1초: 총 데미지 1

2초에 티모의 공격 - 2초: 총 데미지 2, 3초: 총 데미지 3 끝

 

풀이:

1. 비효율적이지만 실제 0~마지막 으로 공격이 들어오는 시간 까지의 경우를 반복문으로 체크 (time out)

class Solution {
    public int findPoisonedDuration(int[] timeSeries, int duration) {
        // 시간 복잡도 O(nm) 공간 복잡도 O(1)
        int ans = 0;
        int demage = 0; // 앞으로 받게될 티모의 독 데미지 수치
        // 0초~timeSeries[] 마지막 값까지의 초를 일일이 카운트 해서 하는 방식
        for(int sec=0;sec<=timeSeries[timeSeries.length-1];sec++) {
            if (demage-- >0) ans++;
            for (int i = 0; i < timeSeries.length; i++) {
                if(sec==timeSeries[i])
                    demage = duration;
            }
        }
        ans+=demage;
        return ans;
    }
}

 

2. one pass 알고리즘 

+ One Pass 알고리즘: 정확히 한번 씩만 값을 읽어서 문제를 해결하는 것

위키백과: https://en.wikipedia.org/wiki/One-pass_algorithm

class Solution {
    public int findPoisonedDuration1(int[] timeSeries, int duration) {
        // 시간 복잡도 O(n) 공간 복잡도(1)
        int ans = 0;
        // 단순히 timeSeries의 배열 값만을 가지고 들어올 데미지 추정
        for(int i=0;i<timeSeries.length-1;i++){
            // 만약 duration(데미지가 지속되는 시간) 보다 들어오는 간격이 더 크다면
            if(timeSeries[i+1]-timeSeries[i]>duration)
                ans+=duration; // 온전히 duration의 데미지 추가
            // duration(데미지가 지속되는 시간) 보다 들어오는 간격이 더 작다면
            else // timeSeries[i]이때 받는 공격의 총 데미지는 시간의 차이만큼이 됨
                ans+=timeSeries[i+1] - timeSeries[i];
        }

        // 마지막에 들어온 공격은 온전히 duration 시간이 변하지 않기 때문에 그대로 추가
        return ans+duration;
    }
}

2-1 위에 코드에서 for 문을 foreach 문으로 바꾸어 속도를 더 개선한 코드

class Solution {
    public int findPoisonedDuration(int[] timeSeries, int duration) {
        int count = (timeSeries[0] == 0) ? 1 : 0;
        int stopTime = 0;
        for (int time : timeSeries) {
            if (stopTime >= time) {
                count -= stopTime - time + 1;
            }
            stopTime = time + duration - 1;
            count += duration;
        }
        return count;
    }
}

 

링크: https://leetcode.com/problems/teemo-attacking/description/

조건:

  • 1 <= area <= 107

의역: 웹 개발자는 웹 페이지를 만들 때 어떤 size로 만들어야 할지 알아야 합니다.

정수 area가 제공될 때 해당 정수 만큼의 넓이를 만들 수 있는 길이와 너비를 정수 배열로 만들어 반환해라. [길이, 너비]

단 (길이 >= 너비) 이여야 하고 길이와 너비의 차이는 가능한 제일 작아야 한다.  

 

의역: area = 4 일때 가능한 경우는 [1,4], [2,2], [4,1] 여기서 위에 나온 

"단 (길이 >= 너비) 이여야 하고 길이와 너비의 차이는 가능한 제일 작아야 한다." 라는 조건을 충족하는 요소는 

[2,2] 임으로 [2,2]를 반환한다.

 

<풀이>

1. 브루트 포스 (단순 반복문을 통해 모든 경우 체크)

class Solution {
    // 시간복잡도 O(n) 공간 복잡도 O(1)
    public int[] constructRectangle(int area) {
        int gap=Integer.MAX_VALUE;
        // 최소 length 길이
        int length=1;
        // 약수를 생각한다면 전체를 검사 할 필요 없이 /2 까지만 검사해도 ok
        for(int i=1;i<=area/2;i++){
            // 약수 라면
            if(area%i==0){
                // gap 차이가 적고 length(길이)가 너비 보다 크다면 length, gap 갱신
                if(Math.abs(area/i-i)<gap&&(area / i)>=i) {
                    length = area / i;
                    gap = area/i-i;
                }
            }
        }
        int ans[]={length,area/length};
        return ans;
    }
}

2. Math.squr()을 통해 정답에 가장 가까운 범위부터 탐색

+ 큰 값 보다는 작은 값을 찾는 방법을 써야 꼬이지 않는다.

해당 문제를 width 가 아닌 length를 구하는 식으로 짠 경우 0의 경우로 인해 큰 값이 아닌 작은 값이 나오는 문제가 생긴다.

class Solution {
    // 시간 복잡도 O(n) 공간 복잡도 O(1)
    public int[] constructRectangle1(int area) {
        int ans[] = new int[2];
        int width = (int)Math.sqrt(area);
        while(area%width!=0){
            width--;
        }
        ans[1] = width;
        ans[0] = area/width;

        return ans;
    }
}

 

링크: https://leetcode.com/problems/construct-the-rectangle/description/

조건:

  • 1 <= s.length <= 105
  • s consists of English letters, digits, and dashes '-'.
  • 1 <= k <= 104

의역: 영문자와 숫자 그리고 - 로만 구성된 문자열 s와 정수 k가 제공될 때

정수 k 보다 짧을 수 있지만 적어도 하나의 문자(영문자 or 숫자)를 포함해야 하는 첫 번째 그룹을 제외하고

그룹: -로 나누어진 문자열 

각 그룹이 정확히 k자를 포함하고 모든 소문자를 대문자로 변환하여 변경된 문자열 s를 반환하라

+ 문자의 위치는 변경할 필요 없다.

의역: 문자 4개로 이루어진 그룹을 만들어야 하므로 -를 제외하면 8문자 이기 때문에 중간에 -을 넣은 문자열을 반환한다.

 

풀이: 

1. StringBuilder 이용 (오른쪽 부터 왼쪽으로 순차적으로 채우기)

class Solution {
    public String licenseKeyFormatting(String s, int k) {
        s.toUpperCase();
        // - 를 제거한 문자열 생성
        char sArray[] = s.toCharArray(); // split 보다 더 빠른 속도로 가능
        StringBuilder deletedDashS = new StringBuilder(""); // 문자열 합치기에 최적
        for (char unit:sArray)
            if(unit!='-')
                deletedDashS.append(unit);
        int count = 0;
        // 뒤에서 부터 카운팅 하여 해당 k번째 자리가 된다면 해당 자리에 - 추가
        for(int i=deletedDashS.length()-1;i>=0;i--){
            if(count!=0&&count%k==0)
                deletedDashS.insert(i+1,'-');
            count++;
        }
        return deletedDashS.toString();
    }
}

1-1 좀더 효율적인 코드 (LeetCode 공식 풀이 코드) 

StringBuilder의 insert를 안써서 그런지 속도 면에서 더 우월했다.

class Solution {
    public String licenseKeyFormatting(String s, int k) {
        char[] chars = s.toCharArray();
        
        int count = 0;
        
        for (int i = 0; i<chars.length; i++) {
            char c = chars[i];
            if (c != '-')
                count++;
        }
        
        StringBuilder b = new StringBuilder(count + (count % k));
        
        int pos = 0;
        
        // first group
        {
            int n = count % k;
            
            count -= n;
            
            while (n > 0) {
                
                while (chars[pos] == '-')
                    pos++;
                
                b.append(Character.toUpperCase(chars[pos]));
                
                pos++;
                n--;
            }
        
            if (pos > 0 && count > 0) {
                b.append('-');
            }
        }
        
        // other groups
        while (count > 0) {
            
            int n = k;
            
            count -= k;
            
            while (n > 0) {
                
                while (chars[pos] == '-')
                    pos++;
                
                b.append(Character.toUpperCase(chars[pos]));
                
                pos++;
                n--;
            }
        
            if (pos > 0 && count > 0) {
                b.append('-');
            }
        }
        
        return b.toString();
    }
}

링크: https://leetcode.com/problems/license-key-formatting/description/

메모: StringBuilder의 insert는 가급적 쓰지 말자.

조건:

  • 1 <= num < 231

의역: 정수의 complement는 이진 표현에서 0을 1로 1을 0으로 뒤집을 때 얻을 수 있는 정수 입니다.

ex: 5 -> 101 (이진법 표시) 뒤집는다면 010 -> 2

정수 하나가 주어질 때 해당 정수의 complement를 반환하시오  

 

+ 이진법 이란? 2의 제곱수로 각 자리수를 표현한 것 현재 실생활에서 일반적으로 쓰고 있는 숫자 체계는 10진법 

ex (이진법): 2^2 x 3 + 2^1 x 3 + 2^0 x 3 = 12 + 6 + 3 = 21

(10진법): 10^1 x 2 + 10^0 x 1 = 20 + 1 = 21 

5 = 101 (이진법 표시)  reverse -> (010) = 2 따라서 2를 반환

1 = 1 (이진법 표시) reverse -> 0 = 0 따라서 0을 반환 

 

풀이: 

1. ^ xor 연산자이용 (서로 다른 비트값이면 1 같다면 0 리턴) 

class Solution {
    // 해당 값의 비트 자리수의 최대 값이 될 수 있는 경우를 반환
    private int getMaxBitValue(int n){
        int i = 0;
        int max = 0;
        // 비트 자리수 계산
        // i = (int) Math.floor((Math.log(n) / Math.log(2)) + 1); 대체 가능
        while(n!=0){
            n/=2;
            i++;
        }
        // 해당 비트 자리수의 최대 값 구하기
        // max = (1 << i) - 1; 대체 가능
        for(int bit=i-1;bit>=0;bit--){
            max+=Math.pow(2,bit);
        }
        return max;
    }
    public int findComplement(int num) {
		// ex 5  5(101)^(111) -> (010)
        return num^getMaxBitValue(num);
    }
}

2. & 연산자와 Integer.highestOneBit(int num) 함수 이용

Integer.highestOneBit(int num) 함수는 해당 정수의 제일 왼쪽의 bit 값의 정보를 반환한다

ex Integer.highestOneBit(5) = 4  

class Solution {
    public int findComplement2(int num) {
    // ex:  5(101) ~5(010) & 4-1(011) = 2(010)
        return ~num & (Integer.highestOneBit(num)-1);
    }
}

 

링크: https://leetcode.com/problems/number-complement/

조건:

  • row == grid.length
  • col == grid[i].length
  • 1 <= row, col <= 100
  • grid[i][j] is 0 or 1.
  • There is exactly one island in grid.

의역: 행과 열로 이루어진 격자판이 있을 때 grid[i][j] = 1이면 해당 지역은 1x1땅이고 grid[i][j] = 0이면 해당 지역은 1x1물 이라고 할때 모든 땅은 대각선이 아닌 수평 수직으로만 이어져 있고 땅 공간 내부에 물이 존재 하지 않는다고 할 때 해당 격자에서 땅의 둘레의 길이를 반환 하라

 

의역: 해당 그림에서 노랑색이 둘레일때 총 16개의 면이 있으므로 16을 반환

의역: 해당 그림에서 노랑색이 둘레일때 총 4개의 면이 있으므로 4을 반환

의역: 해당 그림에서 노랑색이 둘레일때 총 3개의 면이 있으므로 3을 반환

 

<풀이>

1. 브루트 포스

class Solution {
    private int getSurroundLandCount(int [][]grid, int i,int j){
        int land=0;
        if(i-1>=0)
            if(grid[i-1][j]==1) land++;
        if(j-1>=0)
            if(grid[i][j-1]==1) land++;
        if(j+1<= grid[i].length-1)
            if(grid[i][j+1]==1) land++;
        if(i+1<=grid.length-1)
            if(grid[i+1][j]==1) land++;
        return land;
    }
    // 시간 복잡도 O(n*m) 세로*가로 공간 복잡도 O(1)
    public int islandPerimeter(int[][] grid) {
        int ans = 0;
        // 땅이 있는 모든 경우 체크
        for(int i=0;i<grid.length;i++){
            for(int j=0;j<grid[i].length;j++){
                if(grid[i][j]==1) // 해당 땅의 상하좌우에 땅이 있으면 그 수만큼 -
                    ans+=4-getSurroundLandCount(grid,i,j);
            }
        }
        return ans;
    }
}

1-1 좀 더 개선한 버전 (검사 방향(↓, →)을 생각 했을 때 사실상 (↑,← ) 방향만 체크하여도 모든 경우를 체크 하는 것이 가능하다 대신 이 경우에는 위와 다르게 각각의 땅의 둘레를 생각해선 안되고  전체적으로 사라지는 둘레를 고려하여야함)

class Solution {
    // 위에 타일 각각의 둘레를 구하는 접근 에서 전체적인 땅의 둘레를 구하는 접근으로 봐야 함 
    // 시간 복잡도 O(n*m) 세로*가로 공간 복잡도 O(1)
    public int islandPerimeter(int[][] grid) {
        int ans = 0;
        for(int i=0;i<grid.length;i++){
            for(int j=0;j<grid[i].length;j++){
                // 어차피 반복을 통해 모든 오른쪽 아래 경우를 검사 함
                if(grid[i][j]==1){
                    ans+=4;
                    // 위쪽에 땅이랑 이어진 경우 각자의 면에서 하나씩 사라지기 때문에 -2
                    if(i>0&&grid[i-1][j]==1)
                        ans-=2;
                    // 왼쪽에 땅이랑 이어진 경우 각자의 면에서 하나씩 사라지기 때문에 -2
                    if(j>0&&grid[i][j-1]==1)
                        ans-=2;
                    
                }
            }
        }
        return ans;
    }
}

2. DFS

참조한 분의 풀이

class Solution {
    private int getPerimeterDFS(int [][]grid,boolean isVisit[][],int i,int j){
        // 종료 조건: 범위를 벗어나면 끝
        if(i<0||i>= grid.length||j<0||j>=grid[0].length)
            return 1;
        // 종료 조건2: 해당 위치가 물이라면 끝
        if(grid[i][j]==0)
            return 1;
        // 종료 조건3: 이미 방문한 타일이면 끝
        if(isVisit[i][j])
            return 0;
        // 방문 여부 표시
        isVisit[i][j] = true;
	// 재귀적인 방법을 통해 타일 검사
        int count = getPerimeterDFS(grid,isVisit,i-1,j)
                + getPerimeterDFS(grid,isVisit,i,j-1)
                + getPerimeterDFS(grid,isVisit,i+1,j)
                + getPerimeterDFS(grid,isVisit,i,j+1);
        return count;
    }
    public int islandPerimeter(int[][] grid) {
        // 방문 여부를 체크하는 변수
       boolean isVisit[][] = new boolean[grid.length][grid[0].length];
       for(int i=0;i<grid.length;i++){
           for(int j=0;j<grid[i].length;j++){
               if(grid[i][j]==1)
                   return getPerimeterDFS(grid,isVisit,i,j);
           }
       }
       return 0;
    }
}

링크: https://leetcode.com/problems/island-perimeter/

조건:

  • 1 <= s.length <= 104
  • s consists of lowercase English letters.

의역: 문자열 s가 주어질 때 해당 문자열의 일부분(substring)의 반복으로 이루어질 수 있으면 true 아니면 false를 반환해라. (혹은 해당 문자열이 반복되는 패턴으로 구성되어 있으면 true 아니면 false로 해석해도 좋을 것 같다.)

"abab" 는 "ab"의 반복으로 이루어질 수 있으므로 true

"abc"는 "a", "b", "c", "ab", "bc" 의 반복으로는 만들 수 없기 때문에 false

"abcabcabcabc"는 "abc"의 반복으로 만들 수 있기 때문에 true

 

풀이: 

1. 모든 경우 체크 (브루트 포스)

class Solution {
    // 시간복잡도 O(nlog n) // 공간복잡도 O(1)
    private boolean isSubsequence(String targte, String s){
        StringBuilder tmp = new StringBuilder("");
        // targte을 반복하여 이어붙여서 s와 같다면 true 아니면 false
        for(int i=0;i<s.length()/targte.length();i++){
            tmp.append(targte);
        }
        if(tmp.toString().equals(s)) return true;
        else return false;
    }
    public boolean repeatedSubstringPattern(String s) {
        char sArray[] = s.toCharArray();
        String target = "";
        // ex "abcde" 라면 "a" "ab"순으로 확인
        // 만약 target의 문자열의 길이가 절반을 넘어가면 false 이므로 절반까지만 반복
        for(int i=0;i<sArray.length/2;i++){
            target +=sArray[i];
            if(isSubsequence(target,s))
                return true;
        }
        return false;
    }
}

1-1. 좀더 개선한 버전

class Solution {
	// 시간 복잡도 O(log n) 공간 복잡도 O(1);
    public boolean repeatedSubstringPattern1(String s) {
        int length = s.length();
        String copy = "";
        for(int i=1;i<=s.length()/2;i++){
            if(length%i==0){
                copy = s.substring(0,i);
                copy = copy.repeat(length/i);
                if(copy.equals(s))
                    return true;
            }
        }
        return false;
    }
}

2. indexof() 함수 이용 (만약 일정한 패턴으로 해당 문자열이 이루어져 있다면 중간에 반복되는 패턴이 나올 수 있음을 이용)

class Solution {
	// indexOf() 함수의 시간복잡도는 O(n) 공간 복잡도는 O(1)
    public boolean repeatedSubstringPattern2(String s){
        return (s+s).indexOf(s,1)<s.length();
    }
}

indexof()함수의 시간복잡도에 관한 글: https://stackoverflow.com/questions/70902661/does-indexof-in-js-search-all-the-elements-of-an-array-to-execute 

 

 

링크: https://leetcode.com/problems/repeated-substring-pattern/description/

+ Recent posts