Who Says a Pun?(exKMP)

题意

给定一个长度为\(N\)的字符串\(S\)

\(S\)中找到两个互相不重叠且完全相同的连续子串,并输出它的最大长度。

题目链接:https://atcoder.jp/contests/abc141/tasks/abc141_e

数据范围

\(2 \leq N \leq 5 \times 10^3\)

思路

首先介绍Z函数(exKMP):对于一个长度为\(n\)的字符串\(s\)(下标从\(0\)开始)。定义函数\(Z_i\)表示\(s\)\(s[i, n - 1]\)(即以\(s_i\)开头的后缀)的最长公共前缀(LCP)的长度。\(Z\)被称为\(s\)的Z函数。特别地,\(Z[0] = 0\)。Z函数可以通过\(O(n)\)的算法求出。

回到本题。我们只需要枚举起点\(i\),然后求出\(S[i, n - 1]\)的Z函数。然后枚举每个点\(j\)的Z函数值,取最大值即可(注意去掉重叠的情况,即\(Z[j] > j\))。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 5010;

int n;
string ss;
int z[N];

void z_function(string s)
{
    int n = (int)s.length();
    for(int i = 1, l = 0, r = 0; i < n; i ++) {
        if(i <= r && z[i - l] < r - i + 1) z[i] = z[i - l];
        else {
            z[i] = max(0, r - i + 1);
            while(i + z[i] < n && s[z[i]] == s[i + z[i]]) z[i] ++;
        }
        if(i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
    }
}

int main()
{
    cin >> n >> ss;
    int ans = 0;
    for(int i = 0; i < n; i ++) {
        string t = ss.substr(i, n - i);
        z_function(t);
        for(int j = 0; j < n - i; j ++) {
            if(z[j] > j) continue;
            ans = max(ans, z[j]);
        }
    }
    cout << ans << endl;
    return 0;
}
posted @ 2022-06-25 10:58  pbc的成长之路  阅读(32)  评论(0编辑  收藏  举报