热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于最小循环节的几种求法[原创]

 对于任何信息,人类总有一种冲动,就是找到其最本质的组成。例如对于所有的数字,我们会去研究质数,那是因为质数可不可再分解的,于是任何整数都可以写成质因子连乘的形式。对于字符串,看似

 

对于任何信息,人类总有一种冲动,就是找到其最本质的组成。例如对于所有的数字,我们会去研究质数,那是因为质数可不可再分解的,于是任何整数都可以写成质因子连乘的形式。对于字符串,看似无规律,但由于语法上的原因,事实上许多字符串其用到的字符种类是不太多的,也就是说字母表中的26个字母出现的频率是不一样的。于是人类开始研究最小循环节,即某个字符串是不是由某个循环节字符串拼接而成。我们来看下面这个例题:

 

 Pku2406 Power Strings

求一个字符串由多少个重复的子串连接组成,例如ababab由3个ab连接而成,因此答案为3,又例如abcd由1个abcd连接而成,因此答案为1

 

Format

Input

多组数据,以"."代表测试结束 每组数据给出的字符串长度 <=1e6

 

Output

如题

 

样例输入

abcd

aaaa

ababab

.

 

样例输出

1

4

3

 

题解1:

对于这个题,我们设读入的字符串存在字符数组s中,设其长度为len.

于是可以枚举所求的循环节长度为i,即字符数组的前i个字符构成了循环节,然后就可以来进行校验了。由于此处涉及字符的比较,于是使用hash。

#include
#include
#include
#include
using namespace std;
typedef unsigned long long LL;
const LL base=131;
const int N=1000010;
int n;
LL power[N],sum[N];
bool check(LL v,int k) //判断s[1]~s[k]是否是循环节
{
for(register int i=1;i+k-1<=n;i+=k){
if(v!=sum[i+k-1]-sum[i-1]*power[k]) return 0;
}
return 1;
}
int main()
{
power[0]=1;
for(register int i=1;i<=N-10;++i) //hash准备工作
power[i]=power[i-1]*base;
char s[N];
while(scanf("%s",s+1)){
if(s[1]=='.')break;
n=strlen(s+1);
sum[0]=0;
for(register int i=1;i<=n;++i) sum[i]=sum[i-1]*base+LL(s[i]);
for(register int i=1;i<=n;++i){
if(n%i)continue;
LL expect=sum[i];
if(check(expect,i)){
printf("%d\n",n/i);
break;
}
}
}
return 0;
}

  

题解2:

在上种做法中,我们设循环节长度为i ,当然i必然为len的约数。于是整个字符串分成了len/i份。然后逐个逐个比较过去。大胆猜想一下,能否不要比较这么多次呢?

我们来画个图看看,对于字符串s划分如下:

 

 

 

为了区分,这几段标上了不同的颜色。

如果第一段为我们所求的循环节,则我们将s复写一次,并右移i 位

 

 

如果a2—a5这一段等于下面的a1—a4这一段,则可知

A2=a1,a3=a2,a4=a3,a5=a4.

于是循环节为A1.

分析出这个性质后,我们只需要一次字符之间的对比,就可以知道字符串的某个前缀是不是整个字符串的循环节了。

#include
using namespace std;
typedef unsigned long long ull;
char a[2000000];
int len=0;
ull sum[2000000],power[2000000];
ull get(int l,int r)
{
return sum[r]-sum[l-1]*power[r-l+1];
}
int main()
{
while(true)
{
scanf("%s",a+1);
len=strlen(a+1);
if(a[1]=='.'&&len==1)break;
memset(sum,0,sizeof(sum));
memset(power,0,sizeof(power));
for(int i=1;i<=len;i++)
sum[i]=sum[i-1]*193+ull(a[i])+1;
power[0]=1;
for(int i=1;i<=len;i++)
power[i]=power[i-1]*193;
for(int i=1;i<=len;i++) //暴力枚举循环节的长度
{
if(len%i!=0)continue;
else
{
ull a1=get(1,len-i),a2=get(i+1,len);
//注意是取长度为len-i的前缀,看是否等于长度为len-i的后缀
if(a1==a2)
{
printf("%d\n",len/i);//得到循环节的个数
break;
}
}
}
}
return 0;
}

  

Sol3:

 题解2中,减少了比较的次数,看上去似乎没有优化的地步了。我们将眼光转向循环节的长度这个要素。在前面的做法中,我们都只要求循环节长度i为总长度len的约数即可,于是划分的段数 k=len/i,完全没有考虑读入字符串的构成这个因素。很明显我们可以统计下字符串中每种字母出现的次数,不妨设之为sum1……sum26,当我们根据循环节将整个字符串划分成k段时,就是将这些字母“均分”到k段中,于是k至多为gcd(len,sum1,sum2….sum26),如果检测不成功,则也应该为 gcd(len,sum1,sum2….sum26)的约数,至此我们较为精确的约束了k范围,代码略过。

 



推荐阅读
  • 本文介绍如何使用MFC和ADO技术调用SQL Server中的存储过程,以查询指定小区在特定时间段内的通话统计数据。通过用户界面选择小区ID、开始时间和结束时间,系统将计算并展示小时级的通话量、拥塞率及半速率通话比例。 ... [详细]
  • 丽江客栈选择问题
    本文介绍了一道经典的算法题,题目涉及在丽江河边的n家特色客栈中选择住宿方案。两位游客希望住在色调相同的两家客栈,并在晚上选择一家最低消费不超过p元的咖啡店小聚。我们将详细探讨如何计算满足条件的住宿方案总数。 ... [详细]
  • 本文深入探讨了SQL数据库中常见的面试问题,包括如何获取自增字段的当前值、防止SQL注入的方法、游标的作用与使用、索引的形式及其优缺点,以及事务和存储过程的概念。通过详细的解答和示例,帮助读者更好地理解和应对这些技术问题。 ... [详细]
  • 实用正则表达式有哪些
    小编给大家分享一下实用正则表达式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下 ... [详细]
  • 本文探讨了如何在 F# Interactive (FSI) 中通过 AddPrinter 和 AddPrintTransformer 方法自定义类型(尤其是集合类型)的输出格式,提供了详细的指南和示例代码。 ... [详细]
  • 本文详细介绍了Hive中用于日期和字符串相互转换的多种函数,包括从时间戳到日期格式的转换、日期到时间戳的转换,以及如何处理不同格式的日期字符串。通过这些函数,用户可以轻松实现日期和字符串之间的灵活转换,满足数据处理中的各种需求。 ... [详细]
  • 黑马头条项目:Vue 文章详情模块与交互功能实现
    本文详细介绍了如何在黑马头条项目中配置文章详情模块的路由、获取和展示文章详情数据,以及实现关注、点赞、不喜欢和评论功能。通过这些步骤,您可以全面了解如何开发一个完整的前端文章详情页面。 ... [详细]
  • 深入解析SpringMVC核心组件:DispatcherServlet的工作原理
    本文详细探讨了SpringMVC的核心组件——DispatcherServlet的运作机制,旨在帮助有一定Java和Spring基础的开发人员理解HTTP请求是如何被映射到Controller并执行的。文章将解答以下问题:1. HTTP请求如何映射到Controller;2. Controller是如何被执行的。 ... [详细]
  • 本文将探讨2015年RCTF竞赛中的一道PWN题目——shaxian,重点分析其利用Fastbin和堆溢出的技巧。通过详细解析代码流程和漏洞利用过程,帮助读者理解此类题目的破解方法。 ... [详细]
  • 本文详细介绍了如何在Kendo UI for jQuery的数据管理组件中,将行标题字段呈现为锚点(即可点击链接),帮助开发人员更高效地实现这一功能。通过具体的代码示例和解释,即使是新手也能轻松掌握。 ... [详细]
  • 本文详细介绍了 Python 中的条件语句和循环结构。主要内容包括:1. 分支语句(if...elif...else);2. 循环语句(for, while 及嵌套循环);3. 控制循环的语句(break, continue, else)。通过具体示例,帮助读者更好地理解和应用这些语句。 ... [详细]
  • 本文介绍了如何在React和React Native项目中使用JavaScript进行日期格式化,提供了获取近7天、近半年及近一年日期的具体实现方法。 ... [详细]
  • 探讨ChatGPT在法律和版权方面的潜在风险及影响,分析其作为内容创造工具的合法性和合规性。 ... [详细]
  • 本题要求在一组数中反复取出两个数相加,并将结果放回数组中,最终求出最小的总加法代价。这是一个经典的哈夫曼编码问题,利用贪心算法可以有效地解决。 ... [详细]
  • SpringMVC RestTemplate的几种请求调用(转)
    SpringMVCRestTemplate的几种请求调用(转),Go语言社区,Golang程序员人脉社 ... [详细]
author-avatar
休息一下狂想曲
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有