SP687 Repeats

题意

给定字符串,求重复次数最多的连续重复子串

思路

有两个方法

方法1

直接枚举单个循环节的长度 k i ki ki。

然后将字符串划分成 n / k i n/ki n/ki个区间,这样如果只用枚举每一个区间就可以做到 O ( n l o g n ) O(nlogn) O(nlogn)。

考虑到一个重复子串如果循环了 t t t次,那么有至少 t − 1 t-1 t−1个区间是被完全覆盖的。我们只需要用哈希判断相邻的两个区间是不是一样的。

假设现在连续 d d d个区间是一样的,那么我们现在的目标就是确定这个子串到底是循环了 d d d还是 d + 1 d+1 d+1。因为我们已经可以确定循环节,因此左右两边需要添加的字符都是固定的,我们只用再在相邻的两个区间上二分+哈希,就能求出这一段最多扩展的长度,然后就可以判断了。

cpp 复制代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 5e4 + 10;
const ll mod = 998244353;

int n; char s[N];
ll val[N], hsh[N];

ll gethsh(int l, int r) {
    return (hsh[r] - hsh[l - 1] * val[r - l + 1] % mod + mod) % mod;
}

void solve() {
    cin >> n;
    char ss[2];
    val[0] = 1;
    for (int i = 1; i <= n; i++) {
        cin >> ss;
        val[i] = val[i - 1] * 357 % mod;
        hsh[i] = (hsh[i - 1] * 357 + ss[0]) % mod;
    }
    int ans = 1;
    // int ki = 3, l = 5, r;
    for (int ki = 1; ki <= n / 2; ki++) {
        for (int l = 1, r; l <= n; l = r + 1) {
            r = l + ki - 1;
            ll flag = gethsh(l, r);
            while (r + ki <= n && gethsh(r + 1, r + ki) == flag) {
                r = r + ki;
            }
            int zuo, you;
            int L = max(1, l - ki), R = l - 1, mid, pos = l;
            while (L <= R) {
                mid = (L + R) / 2;
                if (gethsh(mid, l - 1) == gethsh(mid + ki, l - 1 + ki)) {
                    pos = mid;
                    R = mid - 1;
                }
                else {
                    L = mid + 1;
                }
            }
            zuo = pos;
            L = r + 1, R = min(n, r + ki), pos = r;
            while (L <= R) {
                mid = (L + R) / 2;
                if (gethsh(r + 1, mid) == gethsh(r + 1 - ki, mid - ki)) {
                    pos = mid;
                    L = mid + 1;
                }
                else {
                    R = mid - 1;
                }
            }
            you = pos;
            ans = max(ans, (you - zuo + 1) / ki);
        }
    }
    cout << ans << endl;
}

int main() {
    // freopen("in.in", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T; cin >> T;
    while (T--) {
        solve();
    }
}

方法二

我采用的是方法一,还没有具体实现过方法二。方法二主要是使用了kmp求循环节的思想。

考虑一个循环子串abaabaaba。

我们可以把它划分成两个相同的子串 a b a a b a abaaba abaaba,长度 l e n = 6 len=6 len=6,而着两个串的endpos之差为 d = 3 d=3 d=3。

如果 d ∣ l e n d|len d∣len,那么就可以判定是由长度为 d d d的循环节构成。

对于sam上面的一个节点,我们先要找出最小的d,用线段树合并来维护。

然后对于在这个节点所代表的字符串长度区间里面找到最大的 d ∣ l e n d|len d∣len,然后就能统计答案了。

相关推荐
一匹电信狗7 小时前
【C++】红黑树详解(2w字详解)
服务器·c++·算法·leetcode·小程序·stl·visual studio
岑梓铭7 小时前
《考研408数据结构》第六章(5.5树的应用)复习笔记
数据结构·笔记·考研·408·ds
不觉晚秋7 小时前
极限挑战之一命速通哈夫曼树
c语言·数据结构··哈夫曼树
寂静山林7 小时前
UVa 11853 Paintball
算法
第七序章8 小时前
【C + +】C++11 (下) | 类新功能 + STL 变化 + 包装器全解析
c语言·数据结构·c++·人工智能·哈希算法·1024程序员节
Theodore_10228 小时前
深度学习(10)模型评估、训练与选择
人工智能·深度学习·算法·机器学习·计算机视觉
五条凪8 小时前
Verilog-Eval-v1基准测试集搭建指南
开发语言·人工智能·算法·语言模型
是店小二呀8 小时前
从“算法思维”到“算子思维”:我在昇腾AI开发中的认知跃迁
人工智能·算法
仰泳的熊猫8 小时前
LeetCode:72. 超级次方
数据结构·c++·算法·leetcode
yuuki2332338 小时前
【数据结构】单链表的实现
c语言·数据结构·后端