Z-algorithm や Suffix Array が使える面白い文字列検索問題!
問題概要
英小文字のみからなる長さ の文字列 が与えられる。次の条件を満たす文字列 の組が何個あるかを答えよ。
- はいずれも空文字ではない
- である
制約
考えたこと
この手の問題では、何かをうまく固定して考えることが肝要だが......色々試行錯誤した末に、
ABC | AC
の区切れ目全探索することにした。左から 個の文字と右側の 個の文字とに分けることにする。このとき、 の左右反転した文字列を として、
- 文字列 において、
S[0:]
とS[i:]
の先頭からの文字の一致の長さを - 文字列 において、
T[0:]
とT[N-i:]
の先頭からの文字の一致の長さを
とする。なお、 の値は、あらかじめ Z 法や Suffix Array などを用いて または の計算量で前処理しておくことで、 の計算量で求められる。
まず、 ならば、条件を満たすように文字列 を取ることは不可能だ。 であるとしよう。
このとき、文字列 の長さは があり得て、文字列 の長さは があり得る。
文字列 の長さの和が となるような長さの組を選べばよい。そのような長さの組の個数は の計算量で求められる。
全体の計算量は または となる。
コード
#include <bits/stdc++.h> using namespace std; vector<int> Zalgo(const string &S) { int N = (int)S.size(); vector<int> res(N); res[0] = N; int i = 1, j = 0; while (i < N) { while (i+j < N && S[j] == S[i+j]) ++j; res[i] = j; if (j == 0) {++i; continue;} int k = 1; while (i+k < N && k+res[k] < j) res[i+k] = res[k], ++k; i += k, j -= k; } return res; } void ARC_055_C() { string S, T; cin >> S; T = S; reverse(T.begin(), T.end()); int N = (int)S.size(); const auto &zs = Zalgo(S); const auto &zt = Zalgo(T); long long res = 0; for (int i = N/2+1; i < N; ++i) { int slen = zs[i]; int tlen = zt[N-i]; if (slen + tlen < N-i) continue; int left = max(1, N-i-tlen); int right = min(N-i-1, slen); res += right - left + 1; } cout << res << endl; } int main() { ARC_055_C(); }