(알고리즘)Manacher's Algorithm (팰린드롬)
Manacher's algorithm은 문자열 내에서 팔린드롬(palindrome,회문)을 찾는것과 관련된 알고리즘이며, 시간 복잡도는 O(n) 이다.(n 은 문자열의 길이)
이 알고리즘은 문자열 S 를 입력으로 받아 다음을 반환한다:
- 문자열
S 와 길이가 같은 정수 배열A , 각A 의 원소A[i] 는i 번째 문자열을 중심으로 하는 가장 긴 팔린드롬의 반지름의 길이.(즉,S[(i−A[i])⋯(i+A[i])] 는 팔린드롬이며,S[(i−A[i]−1)⋯(i+A[i]+1)] 은 팔린드롬이 아니다)
예를 들면, S='banana'라는 입력에 대해 A의 결과는:
b | a | n | a | n | a | |
---|---|---|---|---|---|---|
0 | 0 | 1 | 2 | 1 | 0 |
알고리즘은 다음과 같다:
i 는0 부터n−1(n=|S|) 까지 진행된다j<i 인 모든j 에 대해R=max(j+A[j]) 이라하고, 또한 그러한j 를p 라 하자. 즉,R=p+A[p] i≤R 인지 여부에 따라A[i] 의 초기값이 정해진다i>R 이라면,A[i] 의 초기값은0 이다.i≤R 이라면,i 는p 를 중심으로 한 팔린드롬에 속한다는 이야기이다. 이때p 를 중심으로i 의 대칭점i′ 을 구한다. (즉,i′=2∗p−i )A[i] 의 초기값은min(R−i,A[i′]) 으로 둔다.
A[i] 의 초기값에서부터,S[i−A[i]] 과S[i+A[i]] 가 같을 때까지A[i] 를 증가시키고, 그 다음i 로 넘어간다.
<초기값 정하는 법>
의사코드로 구현하면 다음과 같다:
R = -1
p = -1
for i = 0 to n-1:
if i <= R: A[i] = min(A[2*p - i], R-i)
else: A[i] = 0
while S[i-A[i]-1] == S[i+A[i]+1]:
A[i] = A[i] + 1
if i+A[i] > R:
R = i+A[i], p = i
시간복잡도 분석
A[i]=A[2∗p−i](<R−i) 인 경우
A[i]=R−i(<2[2∗p−i]) A[i]=0
이 두 경우는 i+A[i]>=R 임을 의미한다. 따라서 while문을 다 돌고 나면 R=i+A[i] 가 항상 수행될 것이다. 또한 while문을 한 번 돌때마다 최소한 A[i] 가 1 씩 증가하므로, R 값 역시 while문 수행횟수 이상의 값으로 증가할 것이다. R 값의 정의를 생각해보면, R<n 이고, 절대로 감소하지않는다. 그러므로 while문의 총 실행횟수는 amortizedO(n) 이고, 이는 총 시간복잡도이다.
모든 팔린드롬 구하기
Manacher's algorithm은 각 문자를 중심으로 하는 팔린드롬을 측정하기 때문에, 길이가 짝수인 팔린드롬은 찾을 수 없다. (예:abcddcba) 각 글자 사이에 더미 문자를 끼워넣어서 'a#b#c#d#d#c#b#a'꼴로 만들고 풀면, 짝수길이인 팔린드롬 역시 찾을 수 있다.
참고: https://algospot.com/wiki/read/Manacher's_algorithm
참고: http://articles.leetcode.com/longest-palindromic-substring-part-ii/
참고: http://articles.leetcode.com/longest-palindromic-substring-part-ii/
댓글
댓글 쓰기