KPM算法求字符串的最小周期证明
先给出公式 ans = n - LPS[n-1]
其中ans为最小周期,n为给出的由假设的周期字符串中提取出的子串长度,LPS为前缀函数,n-1为字符串最后的位置下标
证明如下
证明ans = n - LPS[n-1],思路:
(1) 证明特殊情况,即先对完整周期字符串进行证明,这时候的字符串组成是 [1][2][3][4] ,即4个周期拼接,所以由前缀函数的定义,有
[1][2][3] = [2][3][4],所以LPS[n-1] = 3*T,即三个周期,则ans = n(即4*T) - LPS[n-1] = 4*T - 3*T = T;
对于完整周期子串显然成立.
(2) 证明一般情况,即证明非完整周期字符串,假设给出的字符串是 [末部分][1][2][3][前部分] ,即中间有完整的周期,两边是不确定长度的,可能为0.
(为了方便,此处取[末部分] = [e],[前部分] = [b])
1,当len([e]) = len([b]) = 0时,即(1)的情况,显然成立.
2,当len([e]) = 0,len([b]) != 0时此时字符串为 [1][2][3][b],由于[b]是周期的一部分,则[3]中包含[b],有
[1][2][b] = [2][3][b],此时 LPS[n-1] = 2*T + len([b]), n = 3*T + len([b]),显然有
ans = n - LPS[n-1] == 3*T + len([b]) - 2*T - len([b]) = T,成立.
3,当len([b]) = 0,len([e]) != 0时同理.
4,当len([e]) != 0,且len([b]) != 0时,此时字符串为 [e][1][2][3][b],根据2,3,有
[e][1][2][b] = [e][2][3][b],符合前缀函数定义,此时LPS[n-1] = 2*T + len([b+e]),n = 3*T + len([b+e])
显然有ans = n - LPS[n-1] = T,得证
5,当[e][b]内部没有完整的周期时,显然[e][b]可以自己组成最小周期,此时的LPS[n-1] = 0,ans = len([e+b]),为自己,得证
附上例题加以理解:
1,KMP算法模板https://www.luogu.com.cn/problem/P3375
2,字符串周期模板题https://www.luogu.com.cn/problem/P4391
例题1代码解析:
点击查看代码
//背景:刚学LPS函数及其应用KMP,先来道模板题练习,结果发现细节多
//注意:以后我的所有关于KMP的算法的字符串下标均是从0开始(便于与OI-wiki统一,好记忆)
//原理:KMP匹配字符串
//时间复杂度:o(n+m)
#include <bits/stdc++.h>
using namespace std;
void Prefixion(int LPS[],string s)//求生成字符串的前缀函数
{
LPS[0] = 0;//初始化前缀
for (int i = 1,big = s.size();i<big;i++)//遍历字符串
{
int j = LPS[i-1];//获取上一个位置的LPS
while(j>0&&s[j] != s[i]) j = LPS[j-1];//寻找第一个使得当前位置i的前缀性质仍满足的j
if(s[j] == s[i]) j++;//如果是因为相等退出循环的,j是位置,那么长度为j+1
LPS[i] = j;//记录当前位置的LPS
}
}
void KMP()//KMP算法
{
string text,pattern;
cin>>text>>pattern;//输入文本字符串及模板字符串(待查找的字符串)
string cur = pattern + '#' + text;//生成新的字符串
int s1 = pattern.size();//计算模板字符串的长度
int s2 = text.size();//计算文本字符串的长度
int LPS[s2+s1+1];//注意这里LPS的数组大小,如果开小了退不出函数(我也不知道为什么)
Prefixion(LPS,cur);//求生成字符串的前缀函数
vector<int> occurrence;//记录文本字符串匹配成功的起始位置
for (int i = s1+1;i<=s2+s1;i++)//从生成字符串的文本位置开始遍历
{
if(LPS[i] == s1) occurrence.push_back(i-2*s1);//如果满足当前情况,即记录下成功匹配的位置(相对文本):当前位置 - 2*模板长度
}
for (auto v:occurrence) cout<<v+1<<"\n";//依次输出位置(相对文本的)
for (int i = 0;i<s1;i++) cout<<LPS[i]<<" ";//输出每个前缀的函数值
cout<<"\n";
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t = 1;
while(t--)
{
KMP();
}
return 0;
}
例题2代码解析:
点击查看代码
//背景:字符串的周期性问题,一开始不太理解,后来看了题解自己画图推导出来了
//本题题意:本题给出的是重复拼接字符串子串,求最小周期,也就是说假定了原字符串是周期函数,给分析证明提供了思路
//公式:ans = n - LPS[n-1]
#include <bits/stdc++.h>
using namespace std;
void Prefixion(int LPS[],string s)//前缀函数
{
LPS[0] = 0;//不要忘了初始化
for (int i = 1;s[i] != '\0';i++)
{
int j = LPS[i-1];
while(j > 0&&s[j] != s[i]) j = LPS[j-1];
if(s[j] == s[i]) j++;
LPS[i] = j;
}
}
void Solve()
{
int n;
string s;
cin>>n;//输入子串长度
cin>>s;//输入字符串
int LPS[n];//定义前缀函数
Prefixion(LPS,s);//求前缀函数
cout<<n - LPS[n-1]<<"\n";//输出最小周期
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t = 1;
while(t--)
{
Solve();
}
return 0;
}
/*证明ans = n - LPS[n-1],思路:
(1)证明特殊情况,即先对完整周期字符串进行证明,这时候的字符串组成是 [1][2][3][4] ,即4个周期拼接,所以由前缀函数的定义,有
[1][2][3] = [2][3][4],所以LPS[n-1] = 3*T,即三个周期,则ans = n(即4*T) - LPS[n-1] = 4*T - 3*T = T;
对于完整周期子串显然成立.
(2)证明一般情况,即证明非完整周期字符串,假设给出的字符串是 [末部分][1][2][3][前部分] ,即中间有完整的周期,两边是不确定长度的,可能为0.
(为了方便,此处取[末部分] = [e],[前部分] = [b])
1,当len([e]) = len([b]) = 0时,即(1)的情况,显然成立.
2,当len([e]) = 0,len([b]) != 0时此时字符串为 [1][2][3][b],由于[b]是周期的一部分,则[3]中包含[b],有
[1][2][b] = [2][3][b],此时 LPS[n-1] = 2*T + len([b]), n = 3*T + len([b]),显然有
ans = n - LPS[n-1] == 3*T + len([b]) - 2*T - len([b]) = T,成立.
3,当len([b]) = 0,len([e]) != 0时同理.
4,当len([e]) != 0,且len([b]) != 0时,此时字符串为 [e][1][2][3][b],根据2,3,有
[e][1][2][b] = [e][2][3][b],符合前缀函数定义,此时LPS[n-1] = 2*T + len([b+e]),n = 3*T + len([b+e])
显然有ans = n - LPS[n-1] = T,得证
5,当[e][b]内部没有完整的周期时,显然[e][b]可以自己组成最小周期,此时的LPS[n-1] = 0,ans = len([e+b]),为自己,得证
码字不易,多多支持!
KPM算法求字符串的最小周期证明的更多相关文章
- UVA.455 Periodic Strings(字符串的最小周期)
Periodic Strings 模板 题意分析 判断字符串的最小周期 代码总览 /* Title:UVA.455 Author:pengwill Date:2016-12-16 */ #includ ...
- kmp的next数组的运用(求字符串的最小循环节)
hdu3746 Cyclic Nacklace Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/ ...
- Uvalive - 3026 Period (kmp求字符串的最小循环节+最大重复次数)
参考:http://www.cnblogs.com/jackge/archive/2013/01/05/2846006.html 总结一下,如果对于next数组中的 i, 符合 i % ( i - n ...
- POJ--2406Power Strings+KMP求字符串最小周期
题目链接:点击进入 事实上就是KMP算法next数组的简单应用.假设我们设这个字符串的最小周期为x 长度为len,那么由next数组的意义,我们知道len-next[len]的值就会等于x.这就是这个 ...
- E - Power Strings,求最小周期串
E - Power Strings Time Limit:3000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u S ...
- ipv4 ipv6 求字符串和整数一一映射的算法 AmazonOrderId
字符串和整数一一映射的算法 公司每人的英文名不同,现在给每个英文名一个不同的数字编号,怎么设计? 走ipv4/6 2/32 2/128就够了,把“网段”概念对应到“表或库”,ip有a_e5类,这概念 ...
- hdu 4333"Revolving Digits"(KMP求字符串最小循环节+拓展KMP)
传送门 题意: 此题意很好理解,便不在此赘述: 题解: 解题思路:KMP求字符串最小循环节+拓展KMP ①首先,根据KMP求字符串最小循环节的算法求出字符串s的最小循环节的长度,记为 k: ②根据拓展 ...
- 【KMP求最小周期】POJ2406-Power Strings
[题意] 给出一个字符串,求出最小周期. [思路] 对KMP的next数组的理解与运用orz ①证明:如果最小周期不等于它本身,则前缀和后缀必定有交叉. 如果没有交叉,以当前的next[n]为最小周期 ...
- sw算法求最小割学习
http:// blog.sina.com.cn/s/blog_700906660100v7vb.html 转载:http://www.cnblogs.com/ylfdrib/archive/201 ...
- poj 3565 uva 1411 Ants KM算法求最小权
由于涉及到实数,一定,一定不能直接等于,一定,一定加一个误差<0.00001,坑死了…… 有两种事物,不难想到用二分图.这里涉及到一个有趣的问题,这个二分图的完美匹配的最小权值和就是答案.为啥呢 ...
随机推荐
- HarmonyOS SDK,助力开发者打造焕然一新的鸿蒙原生应用
鸿蒙生态千帆启航仪式于 1 月 18 日正式启动.从 2019 年 HarmonyOS 正式发布到 2020 年"没有人能够熄灭漫天星光",今天,满天星光终汇成璀璨星河,Harmo ...
- maven 设置阿里镜像[二]
前言 因为我们在国内,网速很慢,所以最好设置一下阿里镜像. 1.在maven中的conf下的setting 修改为: <mirror> <id>alimaven</id& ...
- 重新点亮linux 命令树————文件列表查看命令[二]
前言 整理一下文件查看命令 正文 主要是pwd和ls命令 pwd 这个是一个非常常用的命令,在shell脚本中基本都有,表示的是当前目录. 这是一个非常简单,但是非常实用的命令. 通过使用pwd -- ...
- Git 中 HEAD、工作树和索引之间的区别
一.HEAD 在git中,可以存在很多分支,其本质上是一个指向commit对象的可变指针,而Head是一个特别的指针,是一个指向你正在工作中的本地分支的指针 简单来讲,就是你现在在哪儿,HEAD 就指 ...
- 文本溢出显示省略号css
项目中常常有这种需要我们对溢出文本进行"..."显示的操作,单行多行的情况都有(具体几行得看设计师心情了),这篇随笔是我个人对这种情况解决办法的归纳,欢迎各路英雄指教. 单行 语法 ...
- 力扣1773(java&python)-统计匹配检索规则的物品数量(简单)
题目: 给你一个数组 items ,其中 items[i] = [typei, colori, namei] ,描述第 i 件物品的类型.颜色以及名称. 另给你一条由两个字符串 ruleKey 和 r ...
- 阿里云RemoteShuffleService新功能:AQE和流控
简介:阿里云EMR自2020年推出Remote Shuffle Service(RSS)以来,帮助了诸多客户解决Spark作业的性能.稳定性问题,并使得存算分离架构得以实施.为了更方便大家使用和扩展 ...
- OpenYurt:延伸原生 Kubernetes 到边缘场景下的落地实践
简介: 随着云原生技术的逐步成熟,阿里云容器服务团队在具体落地实践过程中不断探索云原生技术的应用边界.同时随着物联网和 5G 的迅猛发展,传统的边缘计算架构已经不能满足业务发展的需要. 如何基于云原生 ...
- rerank来提升RAG的准确度的策略
RAG(Retrieval-Augmented Generation)是一种结合检索和生成两种技术的模型,旨在通过检索大规模知识库来增强文本生成任务的准确性. 要通过reranking(重排序)来提升 ...
- [GPT] 网页中某些dom内容是通过 js 数据异步渲染的,nodejs 怎么获取网页解析这些数据
要处理使用JavaScript异步渲染内容的网页,您可以在 JavaScript 蜘蛛中使用 Puppeter 或 Playwright 等无头浏览器来获取网页,然后与动态渲染的内容进行交互. 下 ...