TJOI2019甲苯先生和大中锋的字符串

Posted magicduck

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TJOI2019甲苯先生和大中锋的字符串相关的知识,希望对你有一定的参考价值。

题目链接:https://www.luogu.com.cn/problem/P5341

题目大意:给定 (T) 个字符串, 分别求出这 (T) 个字符串中所有恰好出现 (k) 次的子串中 , 出现过最多次数的长度的最大值

solution

对每个字符串 , 先求出它的 (height) 数组

考虑恰好出现 (k) 次的子串 (s) , 对于 (height) 数组中连续的 (h[i] ... h[i + k - 1]) , 其必然满足 (len(s) < min left{h[i] , h[i + 1] , ... ,h[i + k - 1] ight}) , 同时 (s) 的出现次数不应超过 (k) , 因此 (len(s) > maxleft{h[i - 1] , h[i + k] ight}) , 不难看出对于每 (k) 个数 , 满足要求的 (s) 的长度一定连续 , 于是可以用差分进行区间加 , 统计各个长度出现的次数 , 最后遍历找出最大值即可

时间复杂度: (O(T imes nlogn))

code

#include<bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T &FF) {
    int RR = 1; FF = 0; char CH = getchar();
    for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
    for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
    FF *= RR;
}
inline void file(string str) {
    freopen((str + ".in").c_str(), "r", stdin);
    freopen((str + ".out").c_str(), "w", stdout);
}
const int N = 1e5 + 10;
int tax[N], n, m = 'z', rk[N], xt[N], sa[N], ht[N], ki, pre[N];
char ch[N];
void get_sa() {
    memset(tax, 0, sizeof(tax));
    for(int i = 0; i <= n; i++) ht[i] = rk[i] = xt[i] = sa[i] = 0;
    for(int i = 1; i <= n; i++) tax[rk[i] = ch[i]]++;
    for(int i = 1; i <= m; i++) tax[i] += tax[i - 1];
    for(int i = n; i >= 1; i--) sa[tax[rk[i]]--] = i;
    for(int k = 1; k <= n; k <<= 1) {
        int now = 0;
        for(int i = n - k + 1; i <= n; i++) xt[++now] = i;
        for(int i = 1; i <= n; i++)
            if(sa[i] > k) xt[++now] = sa[i] - k;
        for(int i = 1; i <= m; i++) tax[i] = 0;
        for(int i = 1; i <= n; i++) tax[rk[i]]++;
        for(int i = 1; i <= m; i++) tax[i] += tax[i - 1];
        for(int i = n; i >= 1; i--) sa[tax[rk[xt[i]]]--] = xt[i];
        swap(xt, rk); now = rk[sa[1]] = 1;
        for(int i = 2; i <= n; i++)
            rk[sa[i]] = xt[sa[i]] == xt[sa[i - 1]] && xt[sa[i] + k] == xt[sa[i - 1] + k] ? now : ++now;
        m = now; if(n == m) return;
    }
}
void get_height() {
    for(int i = 1; i <= n; i++) rk[sa[i]] = i;
    int j = 0;
    for(int i = 1; i <= n; i++) {
        if(rk[i] == 1) continue;
        if(j != 0) j--;
        while(i + j <= n && sa[rk[i] - 1] + j <= n && ch[sa[rk[i] - 1] + j] == ch[i + j]) j++;
        ht[rk[i]] = j;
    }
}
int main() {
    //file("");
    int T;
    read(T);
    while(T--) {
        cin >> ch + 1;
        n = strlen(ch + 1); m = 'z';
        get_sa(), get_height();
        cin >> ki; ht[n + 1] = ht[0] = 0;
        for(int i = 0; i <= n + 1; i++) pre[i] = 0;
        deque<int> qi; int ans = 0;
        for(int i = 1; i <= ki; i++) {
            while(!qi.empty() && ht[i] <= ht[qi.front()]) qi.pop_front();
            qi.push_front(i);
        }
        for(int i = ki; i <= n; i++) {
            if(i - qi.back() + 1 >= ki) qi.pop_back();
            while(!qi.empty() && ht[i] <= ht[qi.front()]) qi.pop_front();
            qi.push_front(i);
            int lmax = 0, lmin = max(ht[i + 1], ht[i - ki + 1]);
            if(ki == 1) lmax = n - sa[i + ki - 1] + 1;
            else lmax = ht[qi.back()];
            if(lmax > lmin) pre[lmin + 1]++, pre[lmax + 1]--;
        }
        for(int i = 1; i <= n; i++) pre[i] += pre[i - 1];
        for(int i = n; i >= 1; i--)
            if(pre[i] > pre[ans]) ans = i;
        if(ans == 0) ans = -1;
        cout << ans << endl;
    }
    return 0;
}

以上是关于TJOI2019甲苯先生和大中锋的字符串的主要内容,如果未能解决你的问题,请参考以下文章

[TJOI2019]甲苯先生和大中锋的字符串

TJOI2019甲苯先生和大中锋的字符串

「TJOI2019」甲苯先生的滚榜

题解Luogu P5337 [TJOI2019]甲苯先生的字符串

luogu P5337 [TJOI2019]甲苯先生的字符串

「TJOI2019」甲苯先生的滚榜