第八届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;
}
posted @ 2025-05-26 21:48  windfallll  阅读(11)  评论(0)    收藏  举报