[Baekjoon,4354] 문자열 제곱

1 분 소요

주어진 문자열 s가 부분 문자열 a로 최대로 몇 번 반복해서 이을 수 있는지 찾아야 한다. 예를 들어 문자열 $s=abaabaaba$가 있으면 이는 문자열 $a=aba$를 $3$번 이어 만들 수 있다.

접두사와 접미사

이를 찾는 방법은 문자열의 접두사 접미사가 어떻게 일치하는지를 보면 알 수 있다. 위에서 예로 든 문자열 $s$를 보자. $s$는 문자열 $abaaba$가 접두사이며 접미사인 것을 볼 수 있다. 이는 $s$의 길이 $9$에서 접두사와 동시에 접미사인 문자열의 길이 $6$을 뺀 $3$을 $s$의 길이에 나누면 $9\div 3=3$인 것을 알 수 있다.
이번에는 다른 문자열을 보자 $whatwhat$을 보면 이는 $what$이 $2$번 이어져 만들어 진 것을 볼 수 있다. 이 또한 $8\div 4=2$인 것을 알 수 있다.

그렇다면 어떻게 접두사이며 접미사인 문자열을 구할까? 이는 $KMP$알고리즘이 $failure$ 함수를 구하는 과정을 이용하면 된다. 즉, 문자열의 어느 인덱스에서 접두사도 되고 접미사도 되는 부분 일치 테이블을 찾는 과정으로 구한 배열의 마지막 인덱스 값을 확인하면 된다. 그런데 이 값이 문자열 길이의 절반보다 작다면 특정 문자열 $a$를 이어 문자열 $s$를 만들 수 없다.

구현

이를 구현한 코드를 아래에서 볼 수 있다.

#include <iostream>
#include <string>
#include <vector>
using namespace std;

vector<int> getPartialMatch(const string& s)
{
    int n=s.size();
    vector<int> pi(n,0);
    int matced=0;
    for(int i=1;i<n;++i)
    {
        while(matced>0 && s[i]!=s[matced])
            matced=pi[matced-1];
        if(s[i]==s[matced])
        {
            ++matced;
            pi[i]=matced;
        }
    }
    return pi;
}

int solve(const string& s)
{
    vector<int> pi=getPartialMatch(s);
    int len=s.size();
    
    int k=len-pi[len-1];
    if(len%k!=0)
        return 1;
    return len/k;
}

int main()
{
    while(true)
    {
        string s;
        cin>>s;
        if(s==".")
            break;
        cout<<solve(s)<<endl;
    }

    return 0;
}

댓글남기기