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(); }