第八届gxcpc E.Elimination
题意:
选择一个t, 将t与s匹配,匹配后删除s[i+1]之前的字符,只留下s[i+1]- s[n], 并删除t的最后一个字符, 询问能最多进行几次操作。
思路:
我们要明白字符串 t 中的前缀长度越小,越要在s中出现的位置越靠后,因为t是不断缩减的,但是s是不断删除前缀的。因此我们可以遍历所有的后缀t,用 扩展kmp算法 存下t的后缀与前缀的最长匹配长度,然后匹配成功就不断增加需要匹配的长度即cur,从后往前遍历可以保障前缀长度越小越处于s的后面,由此便可得出答案
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N = 2e5 + 10;
int ans = 0;
int n;
string s;
void Z(int j) { //扩展kmp
string t = "";
for (int i = j;i <= n;i ++) t += s[i];
int m = t.size();
vector<int> z(m+1, 0);
z[1] = m;
t = " " + t;
for (int i = 2, l = 0, r = 0;i <= m;i ++) {
if (i <= r) z[i] = min(r - i + 1, z[i - l + 1]);
while (i + z[i] <= m && t[1 + z[i]] == t[i + z[i]]) z[i] ++;
if (z[i] + i - 1 > r) l = i, r = z[i] + i - 1;
}
int cur = 1;
for (int i = m;i >= 1;i --) {
if (z[i] >= cur) {
cur ++;
}
}
ans = max(ans, cur - 1);
}
void solve() {
cin >> n;
cin >> s;
s = " " + s;
ans = 0;
for (int i = 1;i <= n;i ++) {
Z(i);
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T = 1;
cin >> T;
while (T --) solve();
return 0;
}

浙公网安备 33010602011771号