博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
KMP解决最小循环节问题
阅读量:4671 次
发布时间:2019-06-09

本文共 2415 字,大约阅读时间需要 8 分钟。

【题目描述】

给定若干个长度 $\le 10^6$​​ 的字符串,询问每个字符串最多是由多少个相同的子字符串重复连接而成的。如:ababab 则最多有 333 个 ab 连接而成。

【算法】

1、kmp第一步求出字符串的特征向量。若n%(n-nxt[n])==0&&nxt[n](n是字符串长度)则循环节个数为n/(n-nxt[n]),更一般地对每一个位置 $i$ 若n%(n-nxt[n])==0&&nxt[n],则前i个字符循环。

2、也可以用字符串hash,枚举循环节长度 $l$ + $O(1)$ 判定。

【代码】

#include 
using namespace std;int nxt[1000100];char s[1000100];int main() { while(~scanf("%s",s+1)&&s[1]!='.') { int n=strlen(s+1); nxt[1]=0; for(int i=2,j=0;i<=n;i++) { while(j>0&&(j==n||s[i]!=s[j+1])) j=nxt[j]; if(s[i]==s[j+1]) j++; nxt[i]=j; } if(n%(n-nxt[n])==0&&nxt[n]) printf("%d\n",n/(n-nxt[n])); else puts("1"); } return 0;}

【题目描述】

给你一个字符串,它是由某个字符串不断自我连接形成的。但是这个字符串是不确定的,现在只想知道它的最短长度是多少。

【算法】

由于该字符串可能不完全,考虑若非循环串我们将其补全,由于是最小循环节,显然,当前串最后一个字符必然位于最后一个循环节内。同时最后一个字符在匹配串和主串中和补全的最后一个字符相对位置不变,于是 $n-nxt[n]$ 也即最小循环节长度。

【代码】

#include 
using namespace std;int n;int nxt[1000100];char s[1000100];int main() { scanf("%d%s",&n,s+1); nxt[1]=0; for(int i=2,j=0;i<=n;i++) { while(j>0&&(j==n||s[i]!=s[j+1])) j=nxt[j]; if(s[i]==s[j+1]) j++; nxt[i]=j; } int val; for(int k=nxt[n];k;k=nxt[k]) if(k) val=n-k; if(n%val) printf("%d\n",n/val+1); else printf("%d\n",n/val); return 0;}

【题目描述】

串是有限个小写字符的序列,特别的,一个空序列也可以是一个串。一个串 P 是串 A 的前缀,当且仅当存在串 B,使得 A = PB。如果 P≠A 并且 P 不是一个空串,那么我们说 P 是 A 的一个 proper 前缀。

定义 Q 是 A 的周期,当且仅当 Q 是 A 的一个 proper 前缀并且 A 是 QQ 的前缀(不一定要是 proper 前缀)。比如串 ababababab 都是串 abababa 的周期。串 A 的最大周期就是它最长的一个周期或者是一个空串(当 A 没有周期的时候),比如说,ababab 的最大周期是 abab。串 abc 的最大周期是空串。

给出一个串,求出它所有前缀的最大周期长度之和。

【算法】

这道题要求最大周期也就是循环节长度的最大值(原长不算,因为原长非周期)。于是我们考虑因为 $nxt[i]$ 数组记录的是离 i 最近的所能匹配的模式串位置,于是只要找到离 i 最远的能匹配的模式串位置即可。要么先计算出 $nxt[i]=j$ 的值,然后沿nxt数组一直找下去,直到为0的前一个,但是这样会T。于是我们再开一个数组f,记录离 i 最远的所能匹配的模式串位置,可以递推若 $f[j]>0则f[i]=j否则f[i]=j$。

【代码】

#include 
#define ll long longusing namespace std;int n;ll ans;int nxt[1000100],f[1000100];char s[1000100];int main() { scanf("%d%s",&n,s+1); nxt[1]=0; for(int i=2,j=0;i<=n;i++) { while(j>0&&(j==n||s[i]!=s[j+1])) j=nxt[j]; if(s[i]==s[j+1]) j++; nxt[i]=j; if(f[j]>0) f[i]=f[j]; else f[i]=j; if(f[i]) { int len=i-f[i]; if(i%len==0) ans+=(i/len-1)*len; else ans+=i/len*len; } } printf("%lld\n",ans); return 0;}

转载于:https://www.cnblogs.com/Willendless/p/9609445.html

你可能感兴趣的文章
date()---求N个月后的1号
查看>>
机器学习九大挑战(转载)
查看>>
加密流量分析
查看>>
Python字符串方法
查看>>
.NET笔试题(关于迭代的:遍历XML中的FileName)
查看>>
jQuery修改margin
查看>>
thread and process
查看>>
Corner case
查看>>
Anagrams
查看>>
ETL开发
查看>>
POJ 1166 The Clocks (爆搜 || 高斯消元)
查看>>
如何实现一个malloc
查看>>
javascript基础 之 void
查看>>
【DRP】【SQL】-悲观锁-防止多用户同时操作时出现脏数据
查看>>
MRC
查看>>
python_day25__02__异常处理__try---exception—else---finally
查看>>
常见的HTTP错误码
查看>>
python_集合(set)
查看>>
JS --正则表达式
查看>>
归并排序(分治)
查看>>