[白俊]1701お客様


白駿1701顧客
  • https://www.acmicpc.net/problem/1701
  • 解析接尾辞配列

  • コード長:1940 B
    時間:16ミリ秒

  • 任意の文字列に少なくとも2回現れる部分文字列の最長長.
    =各接尾辞について、接尾辞配列の中で自分の前にある配列と最も長い共通接頭辞の長さの中で最も短い値
  • #include <iostream>
    #include <vector>
    #include <string>
    #include <algorithm>
    using namespace std;
    
    //각 접미사들의 첫 t 글자를 기준으로 한 그룹 번호가 주어질 때
    //주어진 두 접미사를 첫 2t 글자를 기준으로 비교한다
    //group[]은 길이가 0인 접미사도 포함한다
    struct Comparator {
    	const vector<int>& group;
    	int t;
    	Comparator(const vector<int>& _group, int _t) : group(_group), t(_t) {}
    
    	bool operator() (int a, int b) {
    		if (group[a] != group[b]) return group[a] < group[b];
    		return group[a + t] < group[b + t];
    	}
    };
    
    //접미사 배열 계산
    vector<int> getSuffixArray(const string& s) {
    	int n = s.size();
    
    	int t = 1;
    	vector<int> group(n + 1);
    	group[n] = -1;
    
    	for (int i = 0; i < n; ++i)
    		group[i] = s[i];
    
    	vector<int> perm(n);
    	for (int i = 0; i < n; ++i)
    		perm[i] = i;
    
    	while (t < n) {
    		Comparator compareUsing2T(group, t);
    		sort(perm.begin(), perm.end(), compareUsing2T);
    
    		t *= 2;
    		if (t >= n) break;
    
    		vector<int> newGroup(n + 1);
    		newGroup[n] = -1;
    
    		for (int i = 1; i < n; ++i) {
    			if (compareUsing2T(perm[i - 1], perm[i])) {
    				newGroup[perm[i]] = newGroup[perm[i - 1]] + 1;
    			}
    			else {
    				newGroup[perm[i]] = newGroup[perm[i - 1]];
    			}
    		}
    		group = newGroup;
    	}
    	return perm;
    }
    
    //중복되는 부분 문자열의 길이 계산
    int commonPrefix(const string& s, int i, int j) {
    	int k = 0;
    	while (i < s.size() && j < s.size() && s[i] == s[j]) {
    		++i; ++j; ++k;
    	}
    	return k;
    }
    
    //중복되는 부분 문자열의 길이 줄 가장 긴 길이 반환
    int longestCommonSubstring(const string& s) {
    	vector<int> a = getSuffixArray(s);
    
    	int ret = 0;
    	for (int i = 0; i < a.size(); ++i) {
    		int cp = 0;
    		if (i > 0) cp = commonPrefix(s, a[i - 1], a[i]);
    	
    		ret = max(ret, cp);
    	}
    	return ret;
    }
    
    int main() {
    	ios_base::sync_with_stdio(false);
    	cin.tie(NULL);
    	cout.tie(NULL);
    
    	string s;
    	cin >> s;
    
    	cout << longestCommonSubstring(s);
    
    	return 0;
    }
    KMPアルゴリズムの解釈

  • コード長:943 B
    時間:96ミリ秒

  • 任意の文字列に少なくとも2回現れる部分文字列の最長長.
    =文字列Sのすべての接尾辞S[i.]部分一致テーブルの最大値
  • #include <iostream>
    #include <string>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    
    vector<int> getPartialMatch(const string& N) {
    	int m = N.size();
    	vector<int> pi(m, 0);
    
    	int begin = 1;
    	int matched = 0;
    	while (begin + matched < m) {
    		if (N[matched] == N[begin + matched]) {
    			++matched;
    			pi[begin + matched - 1] = matched;
    		}
    		else {
    			if (matched == 0) ++begin;
    			else {
    				begin += matched - pi[matched - 1];
    				matched = pi[matched - 1];
    			}
    		}
    	}
    	return pi;
    }
    
    int getLongestPatialMatch(const string& s) {
    	
    	int ret = 0;
    
    	//s[i..]에 대해 부분 일치 테이블 계산
    	for (int i = 0; i < s.size(); ++i) {
    		vector<int> pi = getPartialMatch(s.substr(i));
    
    		for (int j = 0; j < pi.size(); ++j) {
    			ret = max(ret, pi[j]);
    		}
    	}
    	return ret;
    }
    
    int main() {
    	ios_base::sync_with_stdio(false);
    	cin.tie(NULL);
    	cout.tie(NULL);
    
    	string s;
    	cin >> s;
    	cout << getLongestPatialMatch(s);
    
    	return 0;
    }
    📌参考資料
  • 白準1701お客様の回答
    https://zoomkoding.github.io/%EB%B0%B1%EC%A4%80/2019/09/17/baekjoon-1701.html