期間の長さと
トピックリンク:ybt効率的な高度な2-3-3
トピック
文字列を入力して、各プレフィックスの最大期間の長さの合計を見つけるように依頼します。
文字列のサイクルは、それがその部分文字列であるということですが、空の文字列または文字列自体にすることはできません。この部分文字列を1回コピーして、元の文字列を含む新しい文字列を形成します。
アイデア
実際、部分文字列を見つけて再度コピーし、元の文字列を含めます。部分文字列ごとに、最大の長さの部分文字列を見つけます。
したがって、実際には元の文字列を2つの部分に分割し、前者の文字列に後者の文字列を含めます。その場合、サイクルは前のサイクルです。
次に、前のものに後者を含めることができるようにする方法を見てみましょう。
KMPについて考えてみましょう。公開するときはKMPです。
次に、期間の長さを最大化します。つまり、前面をできるだけ大きくし、背面をできるだけ小さくします。
後ろの長さは実際にはKMPの長さですが、この長さをできるだけ短くしたい、つまりKMPを小さくしたいですか?
それは実際に一致することができる最小のものを見つけることです。それは再帰的なfailiを維持することですfail_if a i l私0 00、次にfaili = 0 fail_i = 0f a i l私=0で、ii私は最小です。
もちろん、ユニオン検索の同様のアイデアを使用して記憶することで、時間を短縮することができます。
コード
#include<cstdio>
using namespace std;
int n, j, fail[1000001];
long long ans;
char c[1000001];
int find(int now) {
if (fail[now]) return fail[now] = find(fail[now]);
return now;
}
int main() {
scanf("%d", &n);
scanf("%s", c + 1);
j = 0;
for (int i = 2; i <= n; i++) {
//KMP
while (j && c[i] != c[j + 1]) j = fail[j];
if (c[i] == c[j + 1]) j++;
fail[i] = j;
}
for (int i = 1; i <= n; i++) {
ans += 1ll * i - 1ll * find(i);
}
printf("%lld", ans);
return 0;
}