1605 반복 부분문자열
문제 링크
- 나의 풀이
라빈카프와 이진탐색을 이용하여 푼 풀이이다. 해쉬 충돌로 인해 결국 푸는데는 실패하였다.
- 찾은 충돌 문자열
upsnoywheogetawhvotekuucofllyjisaagqaeafuilnjiuewpkglhkzmwmmhufkunavrccllnauxwatlncxumyiklusfbccoezh
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;
namespace a {
typedef long long ll;
constexpr ll a = 302;
// constexpr ll a = 29;
constexpr ll p = 1000000007;
// constexpr ll p = 10007;
int sLen;
string tmp;
int findSen(const string& s, int l) {
unordered_map<ll, bool> chk;
ll pow_a = 1, a_ed, hVal = 0;
for (int i = l - 1; i >= 0; i--) {
a_ed = pow_a;
hVal = (hVal + s[i] * pow_a) % p;
pow_a = (pow_a * a) % p;
}
chk[hVal] = 1;
for (int i = 1; i <= sLen - l; i++) {
hVal = (hVal - s[i - 1] * a_ed) % p;
if (hVal < 0) {
hVal += p;
}
hVal = (hVal * a) % p;
hVal = (hVal + s[i + l - 1]) % p;
if (l == 25) {
cout << s.substr(i, l) << "\n";
cout << hVal << "\n";
}
if (chk[hVal]) {
if (l > tmp.size()) {
tmp = s.substr(i, l);
cout << "lsize : " << l << "\n";
cout << s.substr(i, l) << "\n";
cout << hVal << "\n";
}
return 1;
} else {
chk[hVal] = 1;
}
}
return 0;
}
int solve(string s) {
int left = 0, right = sLen, mid;
while (left < right) {
mid = (left + right + 1) / 2;
if (findSen(s, mid)) {
left = mid;
} else {
right = mid - 1;
}
}
return left;
}
/* int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
string s;
getline(cin, s);
sLen = stoi(s);
getline(cin, s);
int left = 0, right = sLen, mid;
while (left < right) {
mid = (left + right + 1) / 2;
if (solve(s, mid)) {
left = mid;
} else {
right = mid - 1;
}
}
cout << left << "\n";
} */
} // namespace a
- 나의 풀이 2
Suffix Array를 이용한 Longest Common Prefix를 이용해 풀었다. 특히 Longest Common Prefix 알고리즘은 O((n))으로 바꿔어야 시간초과가 나지 않고 풀 수 있다.
LCP 알고리즘에서 rank를 Comparator의 group벡터를 가져다가 쓰면 안된다. 왜냐하면 모두 같은 문자로 이뤄진 문자열에서 group벡터의 N / 2 위치 이하(미만?)의 그룹값은 모두 같아진다. T == 1일 때 마지막 perm[n -1]가장 작은 그룹값을 갖게 된다.(group[n] = -1 이기 때문) 이후 연쇄적으로 그룹값이 커지고 중간에서 증가가 멈추게 된다.
#include <bits/stdc++.h>
using namespace std;
vector<int> suffixArray;
class Comparator {
int t;
const vector<int>& group;
public:
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);
for (int i = 0; i < n; i++) {
group[i] = s[i];
}
group[n] = -1;
vector<int>& perm = suffixArray;
perm.resize(n);
for (int i = 0; i < n; i++) {
perm[i] = i;
}
while (t < n) {
Comparator comparator2T(group, t);
sort(perm.begin(), perm.end(), comparator2T);
t *= 2;
if (t >= n) {
break;
}
vector<int> newGroup(n + 1);
newGroup[n] = -1;
newGroup[perm[0]] = 0;
for (int i = 1; i < n; i++) {
if (comparator2T(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 longestFrequent(const string& s, int i, int j) {
int ret = 0;
while (i < s.size() && j < s.size() && s[i] == s[j]) {
i++;
j++;
ret++;
}
return ret;
}
vector<int> getLCP(const string& s, const vector<int>& a) {
int n = s.size();
vector<int> ret(n), rank(n);
for(int i = 0; i < n; i++) {
rank[a[i]] = i;
}
int len = 0;
for (int i = 0; i < n; i++) {
int k = rank[i];
if (k != 0) {
int j = a[k - 1];
while (s[i + len] == s[j + len]) {
len++;
}
ret[i] = len;
if (len) {
len--;
}
}
}
return ret;
}
int solve(const string& s, int n) {
getSuffixArray(s);
int ret = 0;
vector<int> LCP = getLCP(s, suffixArray);
for (int i = 0; i < n; i++) {
ret = max(ret, LCP[i]);
}
return ret;
}
int main() {
int n;
string s;
cin >> n;
cin >> s;
cout << solve(s, n) << "\n";
}
댓글남기기