문제


https://www.acmicpc.net/problem/21142


풀이


아이디어를 쥐어짜내듯 풀었다.

일반적인 2, 3개짜리 LCS는 시간 복잡도가 O(n ** 2), O(n ** 3)라 도움이 되지 않을 거라 생각했다.

일부 문자열들의 LCS 길이를 알아도 전체 LCS 길이를 구하는 데 도움이 되지 않을 것 같았다.

뭔진 모르지만 주어진 문자열들의 LCS가 있다고 쳐보자.

이 LCS는 주어진 문자열들 중 어떤 문자열을 뽑던 순방향으로 읽을 때 띄엄띄엄 등장해야 한다.

반대로 생각하면 선행 관계가 주어졌을 때 이 관계들을 만족시키는 문자열을 찾는 것이라고도 볼 수 있다.

이는 위상 정렬과 동일한 맥락이다.

하지만 위상 정렬은 DAG (Directed Acyclic Graph, 사이클 없는 방향 그래프)에서만 가능하므로 사이클을 모두 제거해 줘야 한다.

길이가 k인 n개의 문자열 각각에 대해, 총 k * (k - 1) / 2개의 (앞에 있는 문자, 뒤에 있는 문자) 쌍을 얻을 수 있다.

A부터 차례로 0, 1, …로 치환하면 k x k 인접 행렬 G를 얻을 수 있다.

이때 G[i][j] == G[j][i] == 1이면 사이클을 의미하므로 두 값 모두 0으로 바꾼다.

이후 k <= 26이므로 dfs로 탐색했다.


코드


import sys
sys.setrecursionlimit(10 ** 5)

n, k = map(int, sys.stdin.readline().split())
graph = [[0] * k for _ in range(k)]

for _ in range(n):
    string = list(map(lambda x: ord(x) - 65, sys.stdin.readline().strip()))
    for i in range(k - 1):
        for j in range(i + 1, k):
            graph[string[i]][string[j]] = 1

for i in range(k):
    for j in range(i + 1, k):
        if graph[i][j] == graph[j][i] == 1:
            graph[i][j] = graph[j][i] = 0


dp = [-1] * k


def f(pos):
    if dp[pos] != -1:
        return dp[pos]
    
    res = 1
    for next in range(k):
        if graph[pos][next]:
            res = max(res, 1 + f(next))
    
    dp[pos] = res
    return res


ans = 0
for start in range(k):
    ans = max(ans, f(start))
print(ans)

Leave a comment