[Platinum II] 문자열의 주기 예측 - 1787
성능 요약
메모리: 127040 KB, 시간: 6876 ms
분류
다이나믹 프로그래밍, 문자열, KMP
제출 일자
2025년 9월 30일 16:19:39
문제 설명
알파벳 소문자들로만 이루어진 문자열을 생각하자. 이런 문자열을 읽어 나가다 보면, 문자열의 주기가 예측되는 순간이 있다. 다음과 같은 문자열을 예로 들어 보자.
a b a b a b a
이 문자열을 네 번째 문자까지의 문자열 'a b a b'와, 그 뒤에 남은 'a b a'로 나누어 생각해 볼 수 있다. 이렇게 하면 뒤쪽 문자열은 앞쪽 네 개의 문자 중 세 번째 문자까지가 반복되다가 끝나는 꼴이다.
a b a b a b a
또한, 여섯 번째 문자까지의 문자열 'a b a b a b'와, 그 뒤에 남은 'a'로 나누어서 생각할 수도 있다. 이 경우에도 뒤쪽 문자열은 앞쪽 문자열이 반복되다가 끝나는 꼴이다.
즉, 예시된 문자열은 'a b a b'혹은, 'a b a b a b'가 반복되는 문자열의 일부라고 예상할 수 있는 것이다. 단, 이러한 추정에서 뒤쪽 문자열이 앞쪽 문자열보다 길면 안 된다. 예를 들어 'a b'는 이 문자열의 주기로 예측하기에는 너무 짧다.
이제는 어떤 문자열 S에 대해, 첫 번째 문자부터, i번째 문자까지로 이루어진 부분 문자열 Si를 생각해 보자. 모든 각각에 대해, 위에 예시된 것처럼 문자열의 주기를 추정할 수 있다. 우리가 관심 있는 것은 이렇게 각각에 Si에 대해 추정할 수 있는 주기 중 가장 긴 것의 길이이다. 이를 Pi라고 하자. 예시된 문자열에서 P7은 4와 6중 최댓값인 6이 된다.
길이가 n인 문자열 S가 주어졌을 때, P1 + P2 + ... + Pn을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 문자열 S의 길이 n이 주어진다. (1 ≤ n ≤ 1,000,000) 둘째 줄에 문자열 S가 공백 없이 주어진다.
출력
첫째 줄에, P1 + P2 + ... + Pn의 값을 출력한다.
💡 해결 방법
💻 코드
# https://www.acmicpc.net/problem/1787
import sys
input = sys.stdin.readline
def kmp(all_string, pattern):
table = [0 for _ in range(len(pattern))]
i = 0
for j in range(1, len(pattern)):
while i > 0 and pattern[i] != pattern[j]:
i = table[i - 1]
if pattern[i] == pattern[j]:
i += 1
table[j] = i
return table
n = int(input())
s = input().strip()
ans = kmp(s,s)
result = 0
dp = [-1] * n
for i in range(len(ans)):
if dp[i] == -1:
dp[i] = ans[i]
r = dp[i]
while 1:
if r - 1 >= 0 and ans[r -1] > 0:
if dp[r - 1] == -1:
dp[r - 1] = ans[r - 1]
r = dp[r - 1]
else:
break
if r > 0:
result += (i + 1 - r)
print(result)