【算法竞赛进阶指南】141.周期 题解 KMP 最小循环节

news2024/10/6 8:11:21

题目描述

一个字符串的前缀是从第一个字符开始的连续若干个字符,例如 abaab 共有 5 5 5 个前缀,分别是 aababaabaaabaab

我们希望知道一个 N N N 位字符串 S S S 的前缀是否具有循环节。

换言之,对于每一个从头开始的长度为 i i i i > 1 i>1 i>1)的前缀,是否由重复出现的子串 A A A 组成,即 A A A … A AAA…A AAAA A A A 重复出现 K K K 次, K > 1 K>1 K>1)。

如果存在,请找出最短的循环节对应的 K K K 值(也就是这个前缀串的所有可能重复节中,最大的 K K K 值)。

输入格式

输入包括多组测试数据,每组测试数据包括两行。

第一行输入字符串 S S S 的长度 N N N

第二行输入字符串 S S S

输入数据以只包括一个 0 0 0 的行作为结尾。

输出格式

对于每组测试数据,第一行输出 Test case # 和测试数据的编号。

接下来的每一行,输出具有循环节的前缀的长度 i i i 和其对应 K K K,中间用一个空格隔开。

前缀长度需要升序排列。

在每组测试数据的最后输出一个空行。

数据范围

2 ≤ N ≤ 1000000 2 \le N \le 1000000 2N1000000

输入样例:

3
aaa
4
abcd
12
aabaabaabaab
0

输出样例:

Test case #1
2 2
3 3

Test case #2

Test case #3
2 2
6 2
9 3
12 4

题意重述

编写一个程序,对于每一个前缀子串 s i s_i si,找出它的最短循环节的重复次数。 s i s_i si 的最短循环节是指连续组合能恰好构成 s i s_i si 的最短的子串。例如,对于字符串 “aabaabaa”,“aab” 不是其最短循环节,因为它无法恰好构成原串。

注意,在以下叙述中,下标从1开始。

算法

通过KMP算法可以求得next数组,对于每一个前缀子串 s i s_i si,其最短循环节的长度就是 i − next [ s i ] i - \text{next}[s_i] inext[si]
为什么呢?

首先看图,上下两条线表示同一个字符串,重合的部分表示KMP匹配(前后缀相等的最大长度)。
上面的1就是下面的1(完全相同的部分),而下面的1等于上面的2(前后缀匹配),上面的2等于下面的2,而下面的2等于上面的3…
所以上面的1,2,3,4,5和下面的1,2,3,4,5完全相同。

image-20230530151038382

在不严格要求“恰好”构成时,每一小段都可以视作原串的循环节。然后,我们可以证明,这样的小段就是原串的最短循环节
那么,如何证明呢?

反证: 假设该小段字符串T不是最短循环节,则原串中必然存在T’作为最短循环节。那么对于T’,可以按照上图的方式,将上下两串划分为一个个由T’组成的部分。

image-20230530152735547

此时矛盾出现:如果可以用更小的T’划分,则根据图示,原串的KMP匹配长度就是 n − len ( T ′ ) n - \text{len}(T') nlen(T)。这个长度 n − len ( T ′ ) n - \text{len}(T') nlen(T) 超过了 n − len ( T ) n - \text{len}(T) nlen(T),而 n − len ( T ) n - \text{len}(T) nlen(T) 是原串的最大前后缀匹配长度。所以假设是错误的,也就是说,T确实是最短循环节。

当不严格要求“恰好”构成时, n − len ( T ) = n − next [ n ] n - \text{len}(T) = n - \text{next}[n] nlen(T)=nnext[n] 就是最短循环节的长度。对于每一个前缀子串 s i s_i si,其最短循环节的长度就是 i − next [ i ] i - \text{next}[i] inext[i]。那么,当我们严格要求恰好构成时,又会是怎样呢?

我们可以证明一个引理,一个字符串的任何循环节(除最短循环节外)都是最短循环节的倍数。也就是说,不存在其他可能的模式使得原串能够恰好由其循环构成。因此,如果原串长度能被 l e n ( T ) len(T) len(T) 整除,则存在最短循环节且为 s [ 1 ∼ l e n ( T ) ] s[1\sim len(T)] s[1len(T)],如果不能被整除,则不存在(按题目的要求,不能“恰好”构成就是为不存在)。

引理证明:

反证: 假设原串存在一个子串T’,它不是最短循环节,也不是最短循环节的循环构成(倍数),但是可以循环构成原串。(有点绕)

这就意味着 l e n ( T ′ ) > l e n ( T ) len(T') > len(T) len(T)>len(T),且 l e n ( T ′ ) len(T') len(T) 不是 l e n ( T ) len(T) len(T) 的倍数。根据循环节的定义,T 和 T’ 都可以构成原串。即原串可以写为 T T . . . T A TT...TA TT...TA(T出现 m 次)或 T ′ T ′ . . . T ′ B T'T'...T'B TT...TB(T’出现 n 次)。这里的 A 和 B 可能是空串,或者长度不足一个 T 或 T’ 的部分。满足 m × l e n ( T ) = n × l e n ( T ′ ) m \times len(T) = n \times len(T') m×len(T)=n×len(T)

于是我们可以找到一个更小的循环节,其长度为d,因为
s j = s j + l e n ( T ) = s j + 2 l e n ( T ) = ⋯ = s j + x l e n ( T ) = s j + x l e n ( T ) − l e n ( T ′ ) = s j + x l e n ( T ) − 2 l e n ( T ′ ) = ⋯ = s j + x l e n ( T ) − y l e n ( T ′ ) = s j + d s_j=s_{j+len(T)}=s_{j+2len(T)}= \cdots =s_{j+xlen(T)}=s_{j+xlen(T)-len(T')}=s_{j+xlen(T)-2len(T')}=\cdots =s_{j+xlen(T)-ylen(T')}=s_{j+d} sj=sj+len(T)=sj+2len(T)==sj+xlen(T)=sj+xlen(T)len(T)=sj+xlen(T)2len(T)==sj+xlen(T)ylen(T)=sj+d

此处的 j j j 可以从几乎任意位置开始,只要字符串的长度足够长以支持所描述的周期 (如果不支持,那么表示从j开始的后续部分无法使用T’进行重复构成,这样的情况则不需要讨论。)。

但这与T是最短循环节的假设产生矛盾,假设不成立。故不存在一种循环节使得它既不是最短循环节,也不是最短循环节的倍数。

看明白了,请给我点赞,谢谢(*^▽^*)。

时间复杂度 O ( n ) \mathcal{O}(n) O(n)

KMP+线性扫描, O ( n ) \mathcal{O}(n) O(n)

C++ 代码

#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e6 + 10;
char s[N]; int ne[N];

int main(){
    int T = 1;
    int n;
    while(scanf("%d", &n), n){
        printf("Test case #%d\n", T ++);
        scanf("%s", 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;
        }
        
        for(int i = 1; i <= n; ++ i){
            int t = i - ne[i];
            if(i > t && i % t == 0){  // i>t保证循环节至少出现2次
                printf("%d %d\n", i, i / t);
            }
        }
        puts("");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/589173.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

7.Ansible Modules介绍

什么是Ansible Modules? Ansible模块根据其功能分为不同的组&#xff0c;每个模块提供了一些功能&#xff0c;可以直接使用。 模块官方文档: https://docs.ansible.com/ansible/2.9/modules/modules_by_category.html 系统模块是要在系统级别执行的操作,例如修改系统上的用户…

Springboot +spring security,认证方式---Form表单认证的实现(三)

一.简介 这篇文章来学习下security的认证方式其中的Form表单认证 二.Spring Security的认证方式 2.1什么是认证 认证: 就是用来判断系统中是否存在某用户&#xff0c;并判断该用户的身份是否合法的过程&#xff0c;解决的其实是用户登录的问题。认证的存在&#xff0c;是为…

Spring Boot 如何实现异步消息处理

Spring Boot异步消息处理 在现代应用程序中&#xff0c;异步消息处理是一项至关重要的任务。它可以提高应用程序的性能、可伸缩性和可靠性&#xff0c;同时也可以提供更好的用户体验。Spring Boot提供了多种方式来实现异步消息处理&#xff0c;包括使用Spring AMQP、Spring Ka…

【斯歌X捷普】优秀体验官活动:全民开发的样板企业是这样炼成的

3月22日&#xff0c;上海斯歌与捷普共同举办了一场别出心裁的活动——“产品优秀体验官”颁奖典礼&#xff0c;以表彰对业务流程开发做出突出贡献的捷普员工。值得注意的是&#xff0c;获奖的14名流程开发人员中&#xff0c;有7人并非是专业的IT人员&#xff0c;而是来自业务岗…

搜索引擎变天了!谷歌宣布开放「生成式搜索平台」!AI 大模型颠覆搜索体验

作者 | 小戏、兔子酱 搜索引擎&#xff0c;可能真的要变天了&#xff01; Google 终于要迎来它 25 年来最大的改变&#xff0c;谷歌宣布了开始内测开放【生成式搜索平台&#xff08;Search Generative Experience&#xff0c;SGE&#xff09;】&#xff0c;并逐步舍弃那些甚至是…

Linux中与中断相关的内核数据结构

【摘要】本文树妖详细讲解了Linux中与中断相关的内核数据结构及其内部联系。 八、中断相关的数据结构 8.1 irq_desc 用于表示IRQ描述符的结构定义如下&#xff1a;\linux-2.6.32.63\include\linux\irq.h struct irq_desc {unsigned int irq; //中断号unsigned int …

ipad可以使用其他品牌的手写笔吗?2023年电容笔推荐

现在&#xff0c;人们的生活越来越智能化&#xff0c;有些人已经用平板电脑取代了传统的笔记本。我发现用ipad不管是用来画画还是做笔记非常方便&#xff0c;但是苹果Pencil对于大多数人来说都是昂贵的。据我所知&#xff0c;如果仅仅是为了记录&#xff0c;而不是为了画画的话…

他是98年的卷王,我真的玩不过他····

现在的小年轻真的卷得过分了。前段时间我们公司来了个98年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 最近和他聊了一次天&#xff0c;原来这位小老弟家里…

PKI 基础知识

摘要 本白皮书介绍了加密和公钥基本结构(PKI)的概念和使用 Microsoft Windows 2000 Server 操作系统中的证书服务的基础知识。如果您还不熟悉加密和公钥技术&#xff0c;先阅读本白皮书将有助于理解 Windows 2000 Web 站点上有关这些主题的其它技术白皮书。 引言 Microsoft …

玩转VLAN间路由,教你3个好方法

我的网工朋友大家好啊 在现实网络中&#xff0c;经常会遇到需要跨VLAN相互访问的情况。 很多网工通常会选择一些方法&#xff0c;来实现不同VLAN间主机的相互访问&#xff0c;例如单臂路由。 但是&#xff0c;单臂路由技术中由于存在一些局限性&#xff0c;比如带宽、转发效…

NodeJS基础到入门EXPS⑥

文章目录 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持&#x1f618;前言Express框架 Router路由NEST 方法路由端点使用回调函数数组处理路由使用混合使用函数和函数数组处理路由中间件的分类及用法 1、应用级中间件 2、路…

使用 python 制作自动填写问卷星问卷调查程序

目录 前言环境&#xff1a;代码展示尾语 &#x1f49d; 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 你的问卷星任务还没做完吗&#xff1f;今天教你如何快速把问卷星调查任务给完成。 环境&#xff1a; anaconda5.2.0&#xff08;python3.6.8&#xff09; 编辑器: p…

ASEMI代理KY可控硅BT169的工作原理及应用领域

编辑-Z 本文主要介绍了可控硅BT169的工作原理及其在各个领域的用。首先&#xff0c;我们将详细阐述可控硅BT169的工作原理&#xff0c;包括结构特点、工作过程等&#xff1b;其次&#xff0c;我们将探讨可控硅BT169在家用电器、工业控制、电力电子等领域的应用。 1、可控硅BT1…

RSA加密 多线程读写不安全

转自&#xff1a;&#xff08;一场开源 RSA 库引发的“血案”&#xff09; 导读 RSA 加密算法是一种非对称加密算法&#xff0c;该算法极为可靠&#xff0c;在现有技术条件下&#xff0c;很难破解&#xff0c;因此在软件开发中被广泛使用。你不必担心&#xff0c;本文不会介绍…

如何Debug调试Android程序

当开发过程中遇到一些奇怪的bug&#xff0c;但又迟迟定位不出来原因是什么的时候&#xff0c;最好的解决办法就是调试了。调试允许我们逐行地执行代码&#xff0c;并可以实时观察内存中的数据&#xff0c;从而能够比较轻易地查出问题的原因。总结一下使用Android Studio来调试A…

又双叒添新证书:上海斯歌通过ISO9001和ISO27001认证

近期&#xff0c;上海斯歌顺利通过权威机构审查&#xff0c;正式成为ISO9001质量管理体系和ISO27001信息管理安全体系双重认证企业。 可以说ISO9001及ISO27001的认证&#xff0c;既是斯歌坚持管理标准化、程序化、规范化的成果&#xff1b;也是国际标准化组织&#xff08;ISO&a…

Vue3通透教程【十六】TS自动编译

文章目录 &#x1f31f; 写在前面&#x1f31f; 自动编译&#x1f31f; 编译器的配置文件&#x1f31f; 写在最后 &#x1f31f; 写在前面 专栏介绍&#xff1a; 凉哥作为 Vue 的忠实 粉丝输出过大量的 Vue 文章&#xff0c;应粉丝要求开始更新 Vue3 的相关技术文章&#xff0…

Apache的配置与应用(构建web、日志分割及AWStats分析系统)

Apache的配置与应用 一、构建虚拟Web主机二、httpd服务支持的三种虚拟机类型1、基于域名的虚拟主机2、基于IP地址的虚拟主机3、基于端口的虚拟主机 三、构建web虚拟目录与用户授权限制1、创建用户认证数据文件2、添加用户授权配置3、验证用户访问权限4、在客户机中浏览器访问 四…

F牌独立站都有哪些收款方式?各有什么优缺点?

最近几个月以来&#xff0c;FP独立站的收款支付问题变得非常焦灼&#xff0c;不少跨境卖家忧心忡忡&#xff0c;害怕自己收不了款血本无归。今天&#xff0c;我跟大家介绍几种FP独立站的收款方式&#xff0c;以及解析他们各有哪些优缺点&#xff0c;方便卖家选择。 一、TT电汇 …

Go GPM 调度器介绍

Go GPM 调度器介绍 1 简介 ​ 这几天在学习Go的GPM机制&#xff0c;于是就整理了一下收集的资料分享给大家&#xff0c;文章末尾有原文链接。主要介绍了Go在运行时调度器的基本实现逻辑和演变过程。 ​ 2 什么是Go调度器 ​ Go调度器很轻量也很简单&#xff0c;足以撑起gorout…