문자열을 적절히 쪼갠 후 하나로 합칠 때, 사전상으로 가장 앞에 오는 단어를 구하는 문제이다.

한 문자내의 쪼갠 문자 순서는 바뀌면 안된다

 

먼저 아이디어는 각 단어를 가리키는 포인터가 각각 존재하며,

맨 앞부분만 서로 비교했을 때 가장 작은 값을 stringbuilder에 하나씩 붙인다.

 

처음에는 단순히 사전순, 길이순으로 정렬해서 풀었는데, 이 경우는 ABCCC, BACCC인 경우 ABABCCCCCC 예외케이스가 나온다.

이는 BAA, BA 테스트 케이스를 해결하기 위해 문자열의 길이를 함께 봤기 때문에 생긴 예외케이스이다.

import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();

		List<String> string = new ArrayList<String>();
		int index[] = new int[n];
		int length[] = new int[n];

		PriorityQueue<Alphabet> queue = new PriorityQueue<Alphabet>();
		for (int i = 0; i < n; i++) {
			string.add(in.next());
		}
		string.sort(Comparator.comparing(String::length).reversed().thenComparing(String::compareTo));

		for (int i = 0; i < n; i++) {
			queue.add(new Alphabet(i, string.get(i).charAt(index[i])));
			length[i] = string.get(i).length();
		}

		StringBuilder sb = new StringBuilder("");
		while (!queue.isEmpty()) {
			Alphabet alphabet = queue.poll();

			sb.append(alphabet.ch);

			int nextIndex = alphabet.index;
			if (index[nextIndex] < length[nextIndex] - 1) {
				index[nextIndex]++;
				queue.add(new Alphabet(nextIndex, string.get(nextIndex).charAt(index[nextIndex])));
			}
		}
		System.out.println(sb.toString());
	}
}

class Alphabet implements Comparable<Alphabet> {
	int index;
	char ch;
	int length;

	public Alphabet(int index, char ch) {
		this.index = index;
		this.ch = ch;
	}

	@Override
	public int compareTo(Alphabet o) {
		if (ch == o.ch) {
			return Integer.compare(index, o.index);
		}
		return Character.compare(ch, o.ch);
	}

	@Override
	public String toString() {
		return "(" + index + ", " + ch + ")";
	}
}

 

그렇다면 문자열을 큐에 넣을때마다 정렬해야 한다는 소리인데

문자열 자체를 맨 앞 부분을 하나씩 제거해야하기 때문에,

연산을 할 때마다 새로운 객체를 반환하는 불변객체 String보다는 StringBuffer을 사용했다.

import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();

		PriorityQueue<StringBuffer> queue = new PriorityQueue<StringBuffer>(new BufferComparator());

		for (int i = 0; i < n; i++) {
			queue.add(new StringBuffer(in.next()));
		}

		while (!queue.isEmpty()) {
			//System.out.println(queue);

			StringBuffer alphabet = queue.poll();

			System.out.print(alphabet.charAt(0));
			alphabet.deleteCharAt(0);

			if (alphabet.length() > 0) {
				queue.add(alphabet);
			}
			alphabet = null;
		}
		System.out.println();
	}
}

class BufferComparator implements Comparator<StringBuffer> {

	public int compare(StringBuffer s1, StringBuffer s2) {
		int min = Math.min(s1.length(), s2.length());
		for (int i = 0; i < min; i++) {
			if (s1.charAt(i) > s2.charAt(i)) {
				return 1;
			} else if (s1.charAt(i) < s2.charAt(i)) {
				return -1;
			}
		}
		return Integer.compare(s2.length(), s1.length());
	}
}

 

BufferComparator을 직접 구현해서 정렬해주도록 했다.

결과는 53%에서 시간초과..

 

 

 

결론적으로는 로직 자체는 같으나, 커스텀 Comparator을 사용하지 않고 기본 Comparable로 정렬하여 사용하는 방법이라면 통과가 된다.

문제 조건에서 공백은 없이 알파벳 대문자로만 구성되어 있다고 했기 때문에,

문자 마지막에 'a'를 붙여주면 (ASCII - \0: 0, a: 097, A: 065)

모든 대문자는 'a'보다 작기 때문에 커스텀 Comparator를 사용하지 않고 기본 Comparable을 사용하면서 같은 동작을 하도록 만들 수 있다.

import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();

		PriorityQueue<StringBuffer> queue = new PriorityQueue<StringBuffer>();
		for (int i = 0; i < n; i++) {
			queue.add(new StringBuffer(in.next() + "a"));
		}

		while (!queue.isEmpty()) {
			StringBuffer alphabet = queue.poll();

			System.out.print(alphabet.charAt(0));
			alphabet.deleteCharAt(0);

			if (alphabet.length() > 1) {
				queue.add(alphabet);
			}
			alphabet = null;
		}
		System.out.println();
	}
}

 

 

이 문제에서 주의해야 하는 대표 테스트케이스는 아래와 같다

4 
CCCA 
CCCB 
CCCD 
CCCE

2
ABCCC
BACCC

2
BAA
BA

 


PriorityQueue의 정렬 방법

PriorityQueue 내부의 정렬은 아래와 같은 함수를 이용하여 정렬된다.

정렬 자체에 영향을 주는 가장 큰 요소는 정렬 알고리즘인데, 이는 머지 소트로 동일하다.

    private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x, queue, comparator);
        else
            siftUpComparable(k, x, queue);
    }
    
    private static <T> void siftUpComparable(int k, T x, Object[] es) {
        Comparable<? super T> key = (Comparable<? super T>) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = es[parent];
            if (key.compareTo((T) e) >= 0)
                break;
            es[k] = e;
            k = parent;
        }
        es[k] = key;
    }

    private static <T> void siftUpUsingComparator(
        int k, T x, Object[] es, Comparator<? super T> cmp) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = es[parent];
            if (cmp.compare(x, (T) e) >= 0)
                break;
            es[k] = e;
            k = parent;
        }
        es[k] = x;
    }

왠지는 모르겠지만.. 아래는 기본 Comparable과 custom Comparator이다.

어디서 차이가 발생했는지 시간날 때 compareTo 함수를 좀 더 까봐야겠다...

    int compareTo(AbstractStringBuilder another) { // StringBuffer::compareTo
        if (this == another) {
            return 0;
        }

        byte val1[] = value;
        byte val2[] = another.value;
        int count1 = this.count;
        int count2 = another.count;

        if (coder == another.coder) {
            return isLatin1() ? StringLatin1.compareTo(val1, val2, count1, count2)
                              : StringUTF16.compareTo(val1, val2, count1, count2);
        }
        return isLatin1() ? StringLatin1.compareToUTF16(val1, val2, count1, count2)
                          : StringUTF16.compareToLatin1(val1, val2, count1, count2);
    }
    
    class BufferComparator implements Comparator<StringBuffer> { // Custom BufferComparator

        public int compare(StringBuffer s1, StringBuffer s2) {
            int min = Math.min(s1.length(), s2.length());
            for (int i = 0; i < min; i++) {
                if (s1.charAt(i) > s2.charAt(i)) {
                    return 1;
                } else if (s1.charAt(i) < s2.charAt(i)) {
                    return -1;
                }
            }
            return Integer.compare(s2.length(), s1.length());
        }
    } 

 

 

'알고리즘 공부 > boj' 카테고리의 다른 글

boj 17071: 숨바꼭질 5  (0) 2021.01.31
boj 1322: X와 K  (0) 2021.01.29
boj 16637: 괄호 추가하기  (0) 2021.01.27
boj 1167: 트리의 지름  (0) 2021.01.25
boj 1939: 중량제한  (0) 2021.01.20

+ Recent posts