[POI2006] OKR-Periods of Words——最大周期长度(扩展最小周期长度)

POI2006 OKR-Periods of Words------最大周期长度(扩展最小周期长度)

原题链接(P3435 POI2006 OKR-Periods of Words - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))


字符串的周期

讲这道题之前,我们先聊一聊字符串的周期。我们要明确周期border两个概念

周期

对字符串 s s s和 0 < p ≤ ∣ s ∣ 0<p\leq \left|s \right| 0<p≤∣s∣,若 s i = s i + p si=si+p si=si+p对所有 i ∈ 0 , ≤ ∣ s ∣ − p − 1 i \in 0,\\leq \\left\|s \\right\| -p-1 i∈0,≤∣s∣−p−1 成立,则称 p p p是 s s s的周期

border

对字符串 s s s和 0 < p < ∣ s ∣ 0<p< \left|s \right| 0<p<∣s∣,若s长度为r的前缀和长度为r的后缀相等,就称s长度为r的前缀是s的border

由s由长度为r的border可以推导出 ≤ ∣ s ∣ − r \leq \left|s \right|-r ≤∣s∣−r是s的周期

根据kmp的next数组,可以得到s(下标从0开始)的所有border长度,nextn-1,nextnext\[n-1-1],...

显然s的最小周期 : n − n e x t n − 1 n-nextn-1 n−nextn−1

思路

现在我们回到该题:

该题就是让我们求字符串 s 1 s_1 s1的最小周期

代码

代码如下

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

char c[N];
int ne[N];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    // 字符串下标从1开始
    cin >> n >> c + 1;
    // 获取next数组
    for (int i = 2, j = 0; i <= n; i ++) {
        while (j && c[i] != c[j + 1])
            j = ne[j];
        if (c[i] == c[j + 1]) {
            j ++;
            ne[i] = j;
        }
    }
    cout << n - ne[n];

    return 0;
}

扩展:最大周期长度

让每一个next数组的值都是最短前缀

核心代码

cpp 复制代码
int j = n;
// 转换为最短前缀
while (ne[j]) 
    j = ne[j];
// 记忆化存储
if (ne[i])  ne[i] = j;
// 最大周期长度
len = n - j;

例题

\[POI2006 OKR-Periods of Words](P3435 POI2006 OKR-Periods of Words - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))

分析

求每个子串的最大周期长度

样例分析

bab 2

baba 2

babab 4

bababa 4

bababab 6

babababa 6

参考代码

cpp 复制代码
#include <iostream>

using namespace std;

const int N = 1e6 + 10;
typedef long long LL;

char s[N];
LL ne[N];

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n;
    cin >> n >> s + 1;
    for (int i = 2, j = 0; i <= n; i ++) {
        while (j && s[i] != s[j + 1])
            j = ne[j];
        if (s[i] == s[j+1])
            j ++;
        ne[i] = j;
    }
    LL ans = 0;
    for (int i = 2; i <= n; i ++) {
        int j = i;
        while (ne[j]) {
            j = ne[j];
        }
        if (ne[i])  ne[i] = j;
        ans += i-j;
    }
    cout << ans << endl;

    return 0;
}
相关推荐
倒头就睡的小比特2 天前
算法竞赛C++常用的STL
c++·算法
weilx12342 天前
C++笔记-文件IO-<fcntl.h>
c++
Smileyqp沛沛2 天前
前端?C++ ?较大差异基础罗列
c++·基础·前端转c++
C语言小火车2 天前
C/C++ 为什么需要编译器?
开发语言·c++
旖旎夜光2 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
吞下星星的少年·-·2 天前
C++ 萌新语法入门篇
c++·算法比赛
霍霍的袁2 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
another heaven2 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
程序猿编码2 天前
告别改源码适配模型:纯 C++ 可配置 LLM 推理引擎,全格式全结构兼容
c++·大模型·llm·推理引擎
此生决int2 天前
深入理解C++系列(20)——C++11(下)
开发语言·c++