알고리즘

백준4354 문자열 제곱 - C++ / KMP

머리큰개발자 2022. 3. 6. 20:04

KMP 알고리즘의 응용 문제.

 

문제를 잘 읽고 다시 생각해보면

전체가 될 수 있는 부분 문자열 중 최소 길이를 구하라는 뜻과 같다. 

즉 s = abcabcabcabc 문자열이 있다면

답의 후보는 abc, abcabc 가 되고, 그 중 더 짧은 abc 를 구하라는 얘기이다.

 

KMP 알고리즘은 찾고 싶은 문자열의 접두사/접미사 중 최대 길이의 Index를 미리 구해놓는데, 그 연장선이다.

 

인덱스를 부여하는 것과 이 문제를 연관시켜보자.

1. 부분 문자열은 항상 index 0 부터 시작해야 한다.

             -> abcabcabcabc 라면 부분 문자열은 bca 형태로 만들어질 수 없고 항상 a 부터 시작해야 한다.

2. 부분 문자열의 중간을 건너뛸 수 없다. 

             -> abcabcabcabc 에서 ab(c)abc 형태로 만들 수 없다

             -> s[i] != s[j] (i : s의 index, j : 접두사의 index)) 인 상황이라면 항상 i 전까지가 부분 문자열이 될 수 있다.

3. [전체 문자열 길이]/[부분 문자열 길이]는 항상 나누어 떨어져야 된다. 

 

위 3가지를 보면 쉽게 알 수 있다.

한 번이라도 j(겹치는 부분 문자열의 마지막 index)가 0이 나오면 그것은 나눠 떨어지는 부분 문자열을 가질 수 없다.(2)

항상 처음부터 시작해야하기 때문에 부분 문자열의 길이와 나머지 문자열의 길이의 합은 항상 i 여야 한다.

(-> 부분 문자열의 길이 x n 이 나머지 문자열의 길이이므로 나누어 떨어져야한다.)

 

즉 abcabcabcabc의 경우 KPM알고리즘을 이용한 idx 는 000123456789 이다.

부분 문자열이 될 수 있으려면, 자신의 길이 이후의 index 에 대해 항상 증가해야 한다.(위의 경우 0~2 까지는 될 수 없다는 뜻) 

이 때, 전체가 될 수 있는 가장 짧은 부분 문자열 길이 3과 나머지 길이 9 의 합은 12이며, 나머지 길이의 합은 idx[last] 와 같아야 한다. 

 

#include <cstdio>
#include <string>
#include <iostream>
#include <cstring>
using namespace std;

char input[1000001];
int findIdx[1000001];

int main() {
	while (1) {
		ios::sync_with_stdio(false);
		cin.tie(0);
		cin >> input;
		if (input[0] == '.') break;
		//KMP making idx array;
		int j = 0;
		int i;
		for (i = 1; input[i]; ++i) {
			while (j && input[i] != input[j]) {
				j = findIdx[j - 1];
			}
			if (input[i] == input[j]) {
				findIdx[i] = ++j;
			}
			else {
				findIdx[i] = 0; 
			}
		}
		//verify
		if (i %(i - findIdx[i-1] )==0) {
			printf("%d\n", i / (i - findIdx[i-1]));
		}
		else {
			printf("1\n");
		}

		
	}
	return 0;
}

'알고리즘' 카테고리의 다른 글

백준 9372 상근이의 여행 - C++  (0) 2023.02.22
삼성 Expert Programmer 후기  (9) 2022.09.21
백준 1786 찾기 - C++/KMP  (0) 2022.03.06
백준13510 C++ 풀이 - HLD  (0) 2022.02.18
백준 2449 전구 C++ - DP  (0) 2021.09.03