KMP算法及应用(hdu2087剪花布条 )Power Strings (POJ2046)Cyclic Nacklace(HDU3746)

本文主要是介绍KMP算法及应用(hdu2087剪花布条 )Power Strings (POJ2046)Cyclic Nacklace(HDU3746),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

KMP由Knuth(D.E.Knuth)、Morris(J.H.Morris)和Pratt(V.R.Pratt)三人设计的线性时间字符串匹配算法。所以叫做KMP。。。。。
字符串匹配,就是从一个字符串中查找出另一个字符串所在位置,当然也可能出现查询不到的情况。

比如给出目标字符串 ss:
  • abcabcabce
所要匹配的模式串 s:
  • abcabce
    这里写图片描述
    当匹配到前6位是,都是成功的,但是到第7位就失败了(ss[6]!=s[6]),我们通长的做法是回溯到ss的第2位,然后让s重新开始从第一位匹配,这样的时间复杂度明显是n*m(n和m分别是ss串和s串的长度),所以就可以想能不能用其他方法解决。

以下是我的理解:

1.我们观察模式串的特点:

前三位abc和第4-6位abc 是相同的,如果说当前6位都是匹配的,把s字符串往后移动三位,是不是前三位也是匹配的。
这里写图片描述
虽然我们知道后面一定是匹配的,但是通常情况下,还是得让计算机跑一遍,(虽然是无用功,但是形式还是要走滴,所以就有TLE……不扯了,扎心qaq)

2.再来看这一个字符串

这里写图片描述
目标串(蓝色)的每一位都不相同,但是依然匹配到第六位失败,注意是每一位都不相同,所以当回溯到下一步的时候,很明显,一定没有一个字符是能匹配成功的,所以又做了无用功
这里写图片描述

3.额,,再看一对串

这里写图片描述
这个很有特点,前面的字符都是一样的,只有最后一个不一样,看到最后一个适配后,肯定知道,应该将模式字符向后移动6位嘛(但是计算机没有人的智商,所以我们需要写一个程序然她,他,它,听话)

为了避免不必要的流程,让时间复杂度降到最低,所以,K,M,P,三人总结出,—->KMP

  • ####KMP是如何工作的
    这里写图片描述
    如果是通常算法的话,需要16步比较,而kmp就只要。。。。

为什么能过省略那么多比较过程呢?———KMP的核心next[]跳转表

其实,模式串往往含有一定的信息——前缀包含。
对于模式串而言,其前缀字符串,有可能也是模式串中的非前缀子串,这个问题我称之为前缀包含问题。
如果不理解上述,,请忽略,看下面的解释
这里写图片描述
如图这个模式字符串,后面三位和前面的三位相同,所以就可以姑且理解为前缀包含。。
以模式串a b c a b c a c a b为例,其前缀的4个字符a b c a,正好也是模式串的一个子串a b c (a b c a) c a b,所以当目标串与模式串执行匹配的过程中,如果直到第 8 个字符才匹配失败,同时也意味着目标串当前字符之前的 4 个字符,与模式串的前 4 个字符是相同的,所以当模式串向后移动的时候,可以直接将模式串的第 5 个字符与当前字符对齐,执行比较,这样就实现了模式串一次性向前跳跃多个字符。
next数组就起到能一次性跳跃多个字符的作用
这里写图片描述
next中下标k含义:对于模式串的第 j 个字符 s[j],是所有满足使 s[1···k-1] =s[j-(k-1)···j-1] (k < j) 成立的 k 的最大值。next[j]=k
简单来说就是当前后缀字符中所包含的前缀的最大值
先不管next怎么用,先来看是怎么得到的:

void getnex(int nex[],char *s)
{int len=strlen(s);int i,j=-1;nex[0]=-1;for(i=1;i<len;i++){while(j>-1&&s[j+1]!=s[i])j=nex[j];if(s[j+1]==s[i])j++;nex[i]=j;}
}
//这里用nex表示next数组,在c++中next已经变成保留字符串,所以不能用next当做跳转数组名,否则报错
还有这种写法
void get_nex(int nex[],char *s)
{int len=strlen(s);int i=0,j=-1;nex[0]=-1;while(i!=len)       {if(j==-1||s[i]==s[j])nex[++i]=++j;else  j=nex[j];}
}
//和上面那种写法区别,建议自己试试
看next是如何完成跳转的
int kmp(char *ss,char *s,int nex[])
{getnex(nex,s);int lens=strlen(s);int lenss=strlen(ss);int i,j=-1;for(i=0;i<lenss;i++){while(j>-1&&s[j+1]!=ss[i])j=nex[j];if(s[j+1]==ss[i])j++;if(j==lens-1){return i-lens+1;//找到模式串,返回在在目标串第一个字符位置}}
}

具体看一个问题
问题(hdu2087剪花布条 )
一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢?
Input
输入中含有一些数据,分别是成对出现的花布条和小饰条,其布条都是用可见ASCII字符表示的,可见的ASCII字符有多少个,布条的花纹也有多少种花样。花纹条和小饰条不会超过1000个字符长。如果遇见#字符,则不再进行工作。
Output
输出能从花纹布中剪出的最多小饰条个数,如果一块都没有,那就老老实实输出0,每个结果之间应换行。
Sample Input
abcde a3
aaaaaa aa
#
Sample Output
0
3
这个题的思路很明显,就是要找出目标串中的“花纹字符串”,虽然暴力能过,但还举荐用kmp

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
#include<map>
#include<cmath>using namespace std;
void getnex(char *s,int nex[])
{int j=-1;int len=strlen(s);nex[0]=-1;int i;for(i=1;i<len;i++){while(j>-1&&s[j+1]!=s[i])j=nex[j];if(s[j+1]==s[i])j++;nex[i]=j;}
}
int kmp(char *ss,char *s,int nex[])
{getnex(s,nex);int i,j,lenss,lens;lenss=strlen(ss);lens=strlen(s);j=-1;int ans=0;for(i=0;i<lenss;i++){while(j>-1&&s[j+1]!=ss[i])j=nex[j];if(s[j+1]==ss[i])j++;if(j==lens-1){j=-1;ans++;}}return ans;
}
int main()
{char ss[1005],s[1005];while(~scanf("%s",ss)){if(strcmp(ss,"#")==0) break;scanf("%s",s);int nex[1005];int ans=kmp(ss,s,nex);printf("%d\n",ans);}return 0;
}
循环节问题

什么是循环节?
—->baidu

利用KMP算法中的next值可以求出字符串的循环节,如ababab的循环节为ab,abcd的循环节为abcd,具体做法如下:假设字符串的长度为len,next[len]为字符串的最后一个字符的下一个字符的next值(下标从0开始),如果len % (len - next[len]) == 0,那么循环节的循环次数为len / (len - next[len]),否则为1

循环节问题
Power Strings (POJ2046)
Given two strings a and b we define a*b to be their concatenation. For example, if a = “abc” and b = “def” then a*b = “abcdef”. If we think of concatenation as multiplication, exponentiation by a non-negative integer is defined in the normal way: a^0 = “” (the empty string) and a^(n+1) = a*(a^n).
Input
Each test case is a line of input representing s, a string of printable characters. The length of s will be at least 1 and will not exceed 1 million characters. A line containing a period follows the last test case.
Output
For each s you should print the largest n such that s = a^n for some string a.
Sample Input
abcd
aaaa
ababab
.
Sample Output
1
4
3
Hint
This problem has huge input, use scanf instead of cin to avoid time limit exceed.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
#include<map>
#include<cmath>using namespace std;
char s[1000000+100];
int nex[1000000+100];
void get_nex(int nex[],char *s)
{int len=strlen(s);int i=0,j=-1;nex[0]=-1;while(i!=len){if(j==-1||s[i]==s[j])nex[++i]=++j;else  j=nex[j];}
}
int main()
{int T,i;while(scanf("%s",s)){if(strcmp(s,".")==0) break;int len=strlen(s);get_nex(nex,s);int t=len-nex[len];if(len%t==0&&nex[len]!=0)printf("%d\n",len/t);else printf("%d\n",1);}return 0;
}

还有循环节的扩展问题
Cyclic Nacklace(HDU3746)
CC always becomes very depressed at the end of this month, he has checked his credit card yesterday, without any surprise, there are only 99.9 yuan left. he is too distressed and thinking about how to tide over the last days. Being inspired by the entrepreneurial spirit of “HDU CakeMan”, he wants to sell some little things to make money. Of course, this is not an easy task.

As Christmas is around the corner, Boys are busy in choosing christmas presents to send to their girlfriends. It is believed that chain bracelet is a good choice. However, Things are not always so simple, as is known to everyone, girl’s fond of the colorful decoration to make bracelet appears vivid and lively, meanwhile they want to display their mature side as college students. after CC understands the girls demands, he intends to sell the chain bracelet called CharmBracelet. The CharmBracelet is made up with colorful pearls to show girls’ lively, and the most important thing is that it must be connected by a cyclic chain which means the color of pearls are cyclic connected from the left to right. And the cyclic count must be more than one. If you connect the leftmost pearl and the rightmost pearl of such chain, you can make a CharmBracelet. Just like the pictrue below, this CharmBracelet’s cycle is 9 and its cyclic count is 2:

这里写图片描述
Now CC has brought in some ordinary bracelet chains, he wants to buy minimum number of pearls to make CharmBracelets so that he can save more money. but when remaking the bracelet, he can only add color pearls to the left end and right end of the chain, that is to say, adding to the middle is forbidden.
CC is satisfied with his ideas and ask you for help.
Input
The first line of the input is a single integer T ( 0 < T <= 100 ) which means the number of test cases.
Each test case contains only one line describe the original ordinary chain to be remade. Each character in the string stands for one pearl and there are 26 kinds of pearls being described by ‘a’ ~’z’ characters. The length of the string Len: ( 3 <= Len <= 100000 ).
Output
For each case, you are required to output the minimum count of pearls added to make a CharmBracelet.
Sample Input
3
aaa
abca
abcde
Sample Output
0
2
5

*这个题就是要求给出字符串,,问还差几位,能形成循环节
所以,直接上代码*

#include<iostream>
#include<stdio.h>
#include<cstring>
using namespace std;
char a[100001];
int nexts[100001];
int main()
{int T,len,i,j,nima;while(scanf("%d",&T)!=EOF){getchar();while(T--){scanf("%s",a+1);len=strlen(a+1);i=1;j=0;nexts[1]=0;while(i<=len){if(j==0||a[i]==a[j]){i++;j++;nexts[i]=j;}elsej=nexts[j];}nima=(len+1)-nexts[len+1];if(len%nima==0&&len!=nima)//这里要注意个是循环节的长度不能等于它自己本身就是len!=nimaprintf("%d\n",0);elseprintf("%d\n",nima-len%nima);}}return 0;
}
//别在意nima这个变量名

这次kmp先写到这,讲真的真不好理解。。。。。。可能我太菜 了吧

这篇关于KMP算法及应用(hdu2087剪花布条 )Power Strings (POJ2046)Cyclic Nacklace(HDU3746)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/402676

相关文章

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

C语言函数递归实际应用举例详解

《C语言函数递归实际应用举例详解》程序调用自身的编程技巧称为递归,递归做为一种算法在程序设计语言中广泛应用,:本文主要介绍C语言函数递归实际应用举例的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录前言一、递归的概念与思想二、递归的限制条件 三、递归的实际应用举例(一)求 n 的阶乘(二)顺序打印

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Python Dash框架在数据可视化仪表板中的应用与实践记录

《PythonDash框架在数据可视化仪表板中的应用与实践记录》Python的PlotlyDash库提供了一种简便且强大的方式来构建和展示互动式数据仪表板,本篇文章将深入探讨如何使用Dash设计一... 目录python Dash框架在数据可视化仪表板中的应用与实践1. 什么是Plotly Dash?1.1

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

Java中&和&&以及|和||的区别、应用场景和代码示例

《Java中&和&&以及|和||的区别、应用场景和代码示例》:本文主要介绍Java中的逻辑运算符&、&&、|和||的区别,包括它们在布尔和整数类型上的应用,文中通过代码介绍的非常详细,需要的朋友可... 目录前言1. & 和 &&代码示例2. | 和 ||代码示例3. 为什么要使用 & 和 | 而不是总是使

Python循环缓冲区的应用详解

《Python循环缓冲区的应用详解》循环缓冲区是一个线性缓冲区,逻辑上被视为一个循环的结构,本文主要为大家介绍了Python中循环缓冲区的相关应用,有兴趣的小伙伴可以了解一下... 目录什么是循环缓冲区循环缓冲区的结构python中的循环缓冲区实现运行循环缓冲区循环缓冲区的优势应用案例Python中的实现库