Manacher - Find Palindrom
ManacherPermalink
Manacher 알고리즘은 문자열에서 각 위치에서 좌우로 몇개의 일치하는 문자가 이어지는지 에 검사할 수 있게 해준다.
기본적으로 문자열이 팰린드롬인지 아닌지는 에 검사가 가능하지만, 이걸 이용하면 모든 인덱스에 대해 팰린드롬의 최대 길이를 구할 수 있다.
Manacher는 홀수 길이의 팰린드롬에 동작하는 알고리즘이지만, 추후 살펴볼 문자열의 문자들 사이사이에 더미 문자를 끼워넣어 문자열의 길이를 으로 만들어주면, 짝수 길이 팰린드롬에 대해서도 검사가 가능하다.
함수 정의Permalink
번 째 인덱스에서 좌우로 일치하는 문자들의 최대 길이라고 하자.
예를 들어, 라는 문자열에서 이다.
그렇다면 를 중앙으로 만들어진 팰린드롬의 길이는 이다.
알고리즘Permalink
문자열의 길이가 일 때, 로 진행하며 를 채울 것이다.
현재 를 채울 차례라고 한다면, 까지 모두 계산이 되어있는 상태일 것이다.
이 때, 까지의 인덱스 중, 가 가장 클 때의 를 라 하고 그 때의 를 이라고 하자.
과 의 상대적인 위치가 중요하다.
1. Permalink
일 때는 다음과 같은 상황이다.
사실상 은 최소 이므로 일 때는 항상 일 것이다.
이 상황에선 부터 좌우로 직접 검사하며 를 채워준다.
그러면 가 될 것이고, 가 될 것이며, 상황은 다음과 같다.
2. Permalink
이므로 우린 구간이 팰린드롬인 것을 안다.
의 대칭점이라고 하자.
이다. (2:1 외분점)
2-1 라면 이다.Permalink
왜냐면 에서의 제일 긴 팰린드롬이 를 기준으로 시작되는 팰린드롬의 구간안에 포함되기 때문에 에서의 팰린드롬과 대칭일 것이기 때문이다.
구간과 을 보자.
그렇다 거꾸로 뒤집으면 동일한 구간이기 때문에 의 길이가 안에 포함이 된다면, 그 길이 자체로 와 같음이 보인다.
2-2 라면Permalink
상황은 다음과 같다. 방금 말한 그 구간을 튀어나간 것이다.
이제 에서의 팰린드롬 구간이 이상으로 벗어나기 때문에 그대로 를 쓸 순 없다.
따라서 이 경우엔 로 초기화를 시켜주고 부터 다시 어디까지 이어지나 검사를 해줄 수 있다.
저렇게 초기화를 시켜줌으로 써, 필요없는 검사를 해줄 필요 없이 우리가 계속 갱신해줘나가야 할 을 늘리는 부분부터 알고리즘을 진행하기 때문에 시간복잡도가 이 되는 핵심이라고 볼 수 있다.
2-1과 2-2를 종합해 보았을 때, 우린 를 로 초기화 시켜주면 된다는 결론이 나온다.
구현의 편의성을 위함이다. 밑에 코드를 보면 이해될 것이다.
시간복잡도Permalink
이 증가하는 모양새를 보자. 중 가장 큰 것 이였다.
그렇다면 은 이 늘어날 때 같이 늘어난다.
그런데 의 최대값은 이다.
따라서 시간복잡도는 이다.
구현Permalink
구현은 짧다.
vi manacher(const string &s) {
int n = sz(s), p = 0;
vi ret(n);
for (int i = 1; i < n; i++) {
int r = p + ret[p];
if (r >= i) ret[i] = min(r - i, ret[2 * p - i]);
while (i - ret[i] - 1 >= 0 && s[i - ret[i] - 1] == s[i + ret[i] + 1])
ret[i]++;
if (i + ret[i] >= r) p = i;
}
return ret;
}
s[i+ret[i]+1]
과 s[i-ret[i]-i]
를 계속해서 비교해주면서 ret[i]
를 늘려주고 있다.
i+ret[i]+1
이 문자열 범위내인지는 검사하지 않았는데, 어차피 문자열 마지막이 널 문자로 끝나서 괜찮다.
c++언어가 아닌 언어로 구현한다면 꼭 s[i+ret[i]+1] < len(s)
처럼 검사해주자.
짝수 길이의 팰린드롬Permalink
홀수 길이일 때 라는 문자열이 있다고 하자.
a | b | b | a | b | a |
---|---|---|---|---|---|
0 | 0 | 0 | 1 | 1 | 0 |
라는 식으로 배열이 만들어질 것이다.
그럼 홀수 길이의 팰린드롬들은 잘 검사를 했다고 하고, 짝수 길이는 어떻게 할까?
문자열에 포함되지 않는 어떤 더미 문자를 집어넣는 것이다.
꼭 시작과 끝에 더미 문자로 감싸주자.
abbaba #a#b#b#a#b#a#
# | a | # | b | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 4 | 0 | 0 | 0 | 0 |
이렇게 더미 문자들에서의 값이 결국 짝수 팰린드롬의 길이를 의미하게 된다는 것을 알 수 있을 것이다.
홀수 길이에선 팰린드롬의 길이는 라는 것도 기억해주자.
연습 문제Permalink
기본 문제이다.
Comments