首页 > 其他分享 >G2. Division + LCP (hard version)

G2. Division + LCP (hard version)

时间:2024-05-03 20:11:06浏览次数:16  
标签:子串 Division LCP int hard leq version ans color

G2. Division + LCP (hard version)

This is the hard version of the problem. In this version $l\le r$.

You are given a string $s$. For a fixed $k$, consider a division of $s$ into exactly $k$ continuous substrings $w_1,\dots,w_k$. Let $f_k$ be the maximal possible $LCP(w_1,\dots,w_k)$ among all divisions.

$LCP(w_1,\dots,w_m)$ is the length of the Longest Common Prefix of the strings $w_1,\dots,w_m$.

For example, if $s=abababcab$ and $k=4$, a possible division is $\color{red}{ab}\color{blue}{ab}\color{orange}{abc}\color{green}{ab}$. The $LCP(\color{red}{ab},\color{blue}{ab},\color{orange}{abc},\color{green}{ab})$ is $2$, since $ab$ is the Longest Common Prefix of those four strings. Note that each substring consists of a continuous segment of characters and each character belongs to exactly one substring.

Your task is to find $f_l,f_{l+1},\dots,f_r$.

Input

The first line contains a single integer $t$ ($1 \le t \le 10^4$) — the number of test cases.

The first line of each test case contains two integers $n$, $l$, $r$ ($1 \le l \le r \le n \le 2 \cdot 10^5$) — the length of the string and the given range.

The second line of each test case contains string $s$ of length $n$, all characters are lowercase English letters.

It is guaranteed that the sum of $n$ over all test cases does not exceed $2\cdot 10^5$.

Output

For each test case, output $r-l+1$ values: $f_l,\dots,f_r$.

Example

input

7
3 1 3
aba
3 2 3
aaa
7 1 5
abacaba
9 1 6
abababcab
10 1 10
aaaaaaawac
9 1 9
abafababa
7 2 7
vvzvvvv

output

3 1 0 
1 1 
7 3 1 1 0 
9 2 2 2 0 0 
10 3 2 1 1 1 1 1 0 0 
9 3 2 1 1 0 0 0 0 
2 2 1 1 1 0 

 

解题思路

  比 E 简单()

  如果对每个 $k$ 都用 Division + LCP (easy version) 的方法二分出答案,时间复杂度是 $O(n^2 \log{n})$。可以反过来考虑,如果每个子串的 LCP 都是 $k$,最多可以划分成几个子串?记为 $f_k$。

  当求出 $f_k \, (1 \leq k \leq n)$ 后,对于每个 $i \, (l \leq i \leq r)$ 答案就是满足 $f_k \geq i$ 的最大的 $k$(显然可以通过减少子串的数量使得 LCP 仍是 $k$)。随着 $i$ 的增大 $k$ 必然是逐渐变小的(这是因为满足 $f_k \geq i$ 的 $k$ 的数量只会减少而不增加)。为此满足单调性,可以用双指针求出每个 $i$ 的答案。

  现在的问题是如何求出 $f_k$。对于固定的 $k$,显然 $s_0 \sim s_{k-1}$ 就是每个子串的 LCP,贪心的思路也很明显,如果有 $s_i \sim s_{i+k-1} = s_0 \sim s_{k-1}$,接下来应该从 $i+k$ 开始继续找下一个与 $s_0 \sim s_{k-1}$ 匹配且最近的子串。因为需要与前缀匹配,可以用 Z Algorithm,若 $z_i \geq k$ 则说明从 $i$ 开始长度为 $k$ 的子串与 $s_0 \sim s_{k-1}$ 匹配。

  如果对于每个 $k$ 都用上述做法求出 $f_k$ 时间复杂度是 $O(n^2)$,要优化的地方应该是找到下一个与前缀匹配的位置,即从某个位置 $i$ 开始满足 $z_j \geq k \, (i \leq j < n)$ 的最小的位置 $j$。我们可以先把所有满足 $z_i \geq k \, (0 \leq i < n)$ 的下标存储到 std::set,这样就可以通过二分求得下一个位置了。为此我们可以倒叙枚举 $k$,从而维护出满足 $z_i \geq k$ 的下标。

  当 LCP 等于 $k$ 时,最多可以划分出 $\frac{n}{k}$ 个子串,因此最多需要二分的次数就是 $\sum\limits_{k=1}^{n}{\frac{n}{k}} \approx n \log{n}$。

  AC 代码如下,时间复杂度为 $O(n \log^2{n})$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 2e5 + 5;

char s[N];
int z[N];
vector<int> p[N];
int f[N];

void solve() {
    int n, l, r;
    scanf("%d %d %d %s", &n, &l, &r, s);
    memset(z, 0, n << 2);
    z[0] = n;
    for (int i = 1, j = 1; i < n; i++) {
        if (i < j + z[j]) z[i] = min(z[i - j], j + z[j] - i);
        while (i + z[i] < n && s[i + z[i]] == s[z[i]]) {
            z[i]++;
        }
        if (i + z[i] > j + z[j]) j = i;
    }
    for (int i = 0; i <= n; i++) {
        p[i].clear();
    }
    for (int i = 0; i < n; i++) {
        p[z[i]].push_back(i);
    }
    set<int> st({n});
    for (int i = n; i; i--) {
        st.insert(p[i].begin(), p[i].end());
        f[i] = 0;
        int x = 0;
        while (x < n) {
            x = *st.lower_bound(x);
            if (x < n) f[i]++;
            x += i;
        }
    }
    for (int i = l, j = n; i <= r; i++) {
        while (j && f[j] < i) {
            j--;
        }
        printf("%d ", j);
    }
    printf("\n");
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }
    
    return 0;
}

  官方题解还给了根号分治的做法。主要是当划分的子串数量为 $s$ 时,LCP 为最大不超过 $k = \frac{n}{s}$,所以可以根据 $s$ 的大小选择不同的做法。

  当 $s \leq \sqrt{n}$ 时,可以直接二分答案,记作 $\text{ans}_i$ 表示划分成 $i$ 个子串时的 LCP。这部分的时间复杂度为 $O(n \sqrt{n} \log{n})$。

  而当 $s > \sqrt{n}$ 时,$k = \frac{n}{s} < \sqrt{n}$。直接暴力枚举 $k \, (1 \leq k \leq \sqrt{n})$,然后找到下一个满足 $z_i \geq k$ 的位置也直接暴力枚举。这部分的时间复杂度为 $O(n \sqrt{n})$。考虑如何更新答案,假如当 LCP 等于 $k$ 时最多可以划分成 $c$ 个子串,按理来说对于每个 $1 \leq i \leq c$ 应该更新 $\text{ans}_i \gets \max\{ \text{ans}_i, k \}$。为了避免超时可以先更新 $\text{ans}_c$,最后枚举完所有的 $k$ 时,倒叙枚举来更新 $\text{ans}_i \gets \max\{ \text{ans}_i, \text{ans_}_{i+1} \}$。

  AC 代码如下,时间复杂度为 $O(n \sqrt{n} \log{n})$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 2e5 + 5, B = 450;

int n, l, r;
char s[N];
int z[N];
int ans[N];

int get(int len) {
    int s = 0;
    for (int i = 0; i < n; i++) {
        if (z[i] >= len) {
            s++;
            i += len - 1;
        }
    }
    return s;
}

void solve() {
    scanf("%d %d %d %s", &n, &l, &r, s);
    memset(z, 0, n << 2);
    z[0] = n;
    for (int i = 1, j = 1; i < n; i++) {
        if (i < j + z[j]) z[i] = min(z[i - j], j + z[j] - i);
        while (i + z[i] < n && s[i + z[i]] == s[z[i]]) {
            z[i]++;
        }
        if (i + z[i] > j + z[j]) j = i;
    }
    memset(ans, 0, n + 2 << 2);
    for (int i = 1; i <= B; i++) {
        int l = 0, r = n / i;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (get(mid) >= i) l = mid;
            else r = mid - 1;
        }
        ans[i] = l;
    }
    for (int i = 1; i <= B; i++) {
        int c = get(i);
        ans[c] = max(ans[c], i);
    }
    for (int i = n; i; i--) {
        ans[i] = max(ans[i], ans[i + 1]);
    }
    for (int i = l; i <= r; i++) {
        printf("%d ", ans[i]);
    }
    printf("\n");
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }
    
    return 0;
}

 

参考资料

  Codeforces Round 943 (Div. 3) A - G:https://zhuanlan.zhihu.com/p/695745337

  Codeforces Round 943 (Div. 3) Editorial:https://codeforces.com/blog/entry/129096

标签:子串,Division,LCP,int,hard,leq,version,ans,color
From: https://www.cnblogs.com/onlyblues/p/18171549

相关文章

  • [转帖]10 Hardware Components of Oracle Exadata
    https://docs.oracle.com/en/engineered-systems/exadata-database-machine/dbmso/hardware-components-exadata-db-machine.html#GUID-EBA9369F-A2AB-449F-A361-40F48A5B37C2 OracleExadata consistsofdatabaseservers,storageservers,andthenetworkcomponent......
  • P6123 [NEERC2016] Hard Refactoring 题解
    本题说白了,就是一道big模拟!!!题意不再赘述,我们直接看思路。这里作者借鉴了某差分思想:末尾加空格,用于判断最后一个条件;若只有\(\le\),对给出的数字和数组第一个进行标记。标记的时候要+32769,因为数组中不存在负数下标,以免越界;若只有\(\ge\),就标记给出的数字和数组最后......
  • D2. Reverse Card (Hard Version)
    D2.ReverseCard(HardVersion)Thetwoversionsaredifferentproblems.Youmaywanttoreadbothversions.Youcanmakehacksonlyifbothversionsaresolved.Youaregiventwopositiveintegers$n$,$m$.Calculatethenumberoforderedpairs$(a,b)$......
  • You have an error in your SQL syntax; check the manual that corresponds to your
    在进行增加User对象时采用了PreparedStatement对象,方法executeQuery()和executeUpdate()都是无参方法,所以不要写成prepareStatement.executeQuery(sql)或prepareStatement.executeUpdate(sql)publicintadd(Useruser){Connectionconnection=JDBCTools.getConnecti......
  • 简单解决version 'GLIBC_2.34' not found,version 'GLIBC_2.25' not found
    简单解决version'GLIBC_2.34'notfound,version'GLIBC_2.25'notfound无需手动下载安装包编译前言很多博客都是要手动下载安装包进行编译升级,但这样很容易导致系统崩溃,本博文提供一个简单的方法,参考自博客1,博客2.检查版本strings/usr/lib64/libc.so.6|grepGLIBC_或者......
  • 报错“Please indicate a valid Swagger or OpenAPI version field”
    报错“PleaseindicateavalidSwaggerorOpenAPIversionfield”报错信息PleaseindicateavalidSwaggerorOpenAPIversionfield.Supportedversionfieldsareswagger:"2.0"andthosethatmatchopenapi:3.0.n(forexample,openapi:3.0.0). 原因分析根......
  • CF1967B2 Reverse Card (Hard Version) 题解
    题意:求有多少对\((a,b)\)满足\(b\times\gcd(a,b)\equiv0\pmod{a+b},1\lea\len,1\leb\lem\)。首先我们设\(\gcd(a,b)=G,a=i\timesG,b=j\timesG\),显然有\(\gcd(i,j)=1\)。那么可以把原条件转化为\(j\timesG\)是\((i+j)\)的倍数。因为\(\gcd(i+......
  • Reverse Card (Hard Version)
    事情是这样的,我验了这一场CF。显然我玩原神玩多了有一个很奇怪的、不能过的算法,哦,当然,在我本机可以过。为了展现自己的智慧糖,我写一下。出题人是先发给我了一个限制都是\(n\)的,因此只有这个。\(n,m\)改改就是了。要求\(1\lea\len,1\leb\len\)满足\(a+b\midb\times......
  • ES Validation Failed: 1: this action would add [1] shards, but this cluster c
    [2024-05-01T08:56:52,606][ERROR][o.e.x.i.IndexLifecycleRunner][tools]policy[ilm-history-ilm-policy]forindex[.ds-ilm-history-5-2024.03.28-000001]failedonstep[{"phase":"hot","action":"rollover","name&qu......
  • Intel Pentium III CPU(Coppermine, Tualatin) L2 Cache Latency, Hardware Prefetch
    这几天,偶然的机会想到了困扰自己和其他网友多年的IntelPentiumIII系列处理器缓存延迟(L2CacheLatency),以及图拉丁核心版本是否支持硬件预取(HardwarePrefetch)问题。手头的支持图拉丁核心处理器的i815主板还在正常服役中,铜矿和图拉丁核心处理器也都有,所以就专门做了这一期调查,感......