题解报告:hdu 2087 剪花布条(KMP入门)
Problem Description
一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢?
Input
输入中含有一些数据,分别是成对出现的花布条和小饰条,其布条都是用可见ASCII字符表示的,可见的ASCII字符有多少个,布条的花纹也有多少种花样。花纹条和小饰条不会超过1000个字符长。如果遇见#字符,则不再进行工作。
Output
输出能从花纹布中剪出的最多小饰条个数,如果一块都没有,那就老老实实输出0,每个结果之间应换行。
Sample Input
abcde a3
aaaaaa aa
#
Sample Output
0
3
解题思路:这是一道KMP入门题,设计出这个算法的人真的太聪明了,字符串匹配过程真的太巧妙了!!!这道题就是给你一个主串和一个模式串,要求从主串找出没有交集的模式串的最大数量。一开始很容易想到暴力枚举,不过会超时,时间复杂度是O(nm),还不如花多点时间学习新的算法思想,争取在做题中灵活应用。对于KMP的讲解,目前有一篇大牛写的算是賊好理解了,这个算法时间复杂度是O(n+m),传送门:KMP算法最浅显理解——一看就明白
AC代码一:
#include<bits/stdc++.h>
using namespace std;
char text[],pattern[];//主串,模式串,问主串中能截取出多少个模式串,取出后再从后面的子串中查找
int prefix[],lena,lenb,num; //prefix[i]记录的是前i-1个字符构成的子串中最长公共前后缀长度
void get_prefix_table(){//处理模式串的前缀表
int j=,pos=-;
prefix[]=-;//prefix[0]初始化为-1,-1表示不存在相同的最大前缀和最大后缀,即模式串第一个字符前面不存在子串
while(j<lenb){
if(pos==-||pattern[pos]==pattern[j])prefix[++j]=++pos;//当pos为-1时,++j,++pos表示下一个字符前的子串的最长公共前后缀长度为0
else pos=prefix[pos];//如果当前j指向的字符和pos指向的字符不同,则pos退回到pos指向下标为当前pos位置前面子串的最长公共前后缀长度的字符,再次进行比较
}
}
void kmp_search(){//从主串中能截取出多少模式串,不叠加计算
int i=,j=;//i从主串下标为0开始,j从模式串下标为0开始
while(i<lena){
if(j==-||text[i]==pattern[j])i++,j++;//如果j==-1,说明主串中i位置前面的子串与模式串没有交集,即前者的后缀和后者的前缀不相等,则同时右移一位
else j=prefix[j];//j退回到j指向下标为模式串中当前j位置前面子串的最长公共前后缀长度的字符,再次进行比较
if(j==lenb)num++,j=;//如果j等于模式串的长度,则计数器加1,并且j重置为0,再寻找主串中后面的子串是否还包含模式串
}
}
int main(){
while(~scanf("%s",text)&&strcmp(text,"#")){
scanf("%s",pattern);
memset(prefix,,sizeof(prefix));
num=,lena=strlen(text),lenb=strlen(pattern);
get_prefix_table();
kmp_search();
printf("%d\n",num);
}
return ;
}
代码过程简单分析:如上所示,我们先对模式串进行打印前缀表,然后再调用kmp_search()来与主串匹配。这里给一串模式串abaabcac计算其前缀表。首先我们定义prefix[0]=-1,表示模式串第1个字符前面的子串的最长公共前后缀长度不存在,定义为-1,待循环模式串到j,如果第j个字符pattern[j]!=pattern[pos],pos退回到下标为第pos个字符前面的子串的最长公共前后缀长度,再次比较,如果继续不等,pos将会退回到-1,这时满足if语句则执行prefix[++j]==0,表示下一个字符前面的子串的最长公共前后缀长度为0,为什么是先计算下一个字符的prefix[j]呢?这是为了代码好写,简洁一些,巧妙处理前缀表,所以给出模式串的前缀表如下:
模式串的下标 0 1 2 3 4 5 6 7
模式串 a b a a b c a c
前缀表下标 0 1 2 3 4 5 6 7 8
最长公共前后缀长度 -1 0 0 1 1 2 0 1 0
注意上面前缀表与模式串对应的下标位置。计算好模式串的前缀表之后,开始与主串进行匹配了,用i,j分别指向主串,模式串的当前位置,当匹配失败时,指针i(指向主串)不变,指针j(指向模式串)退回到prefix[j]所指示的位置上重新进行比较,并且当j退至-1时,指针i和指针j需同时增1。即若主串的第i个字符和模式串的第j个字符不等,应从主串的第i+1个字符重新进行匹配。
上面给的大牛博客里讲的很清楚了,为什么j要退回到prefix[j],这里谈谈我的学习心得:prefix[j]记录的是前j-1个字符组成的子串的最长前后缀公共长度。举个栗子:仍用上面的模式串,假设现在我们要求最后的'c'字符下的prefix[8],怎么求呢?已知prefix[7]=1,说明'c'字符前面的字符串abaabca其最长公共前后缀长度为1,这时我们加上了字符'c',那么只需比较最前面的第1个字符'a'后面字符'b'是否等于当前字符'c',即判断pattern[j=1]==pattern[i=7]?如果相等的话就执行prefix[7+1]=prefix[8]=1+1=2(表示最后一个字符的下一个位置前面的子串的最长公共前后缀长度是2),否则j=prefix[j=1]=0,很明显,pattern[j=0]!=pattern[i=7],所以j继续退回j=prefix[j=0]=-1,这时说明字符'c'已不能与第一个字符'a'前面的字符进行匹配,满足if语句,即prefix[8]=-1+1=0。接下来进行模式串与主串的匹配,其匹配模式和计算前缀表所用的方法基本一样,多了一步判断j(j是从0开始计算的)是否达到模式串尾,即判断j==lenb?如果是,计数器加1,并且j重置为0,即从模式串第一个字符开始,再在主串中剩下的子串中查找模式串。
解法2:这道题要求找主串中含有的不相交模式串的数量,那么可以运用库函数strstr()来计数num。
strstr 语法:头文件#include <string.h>
char *strstr( const char *str1, const char *str2 );
功能:函数返回一个指针,它指向字符串str2 首次出现在字符串str1中的位置,如果没有找到,返回NULL。
解题思路:通过这个函数的特点,因为返回的是地址,所以我们定义一个字符指针p来指向接受返回地址,如果找到的话,加上模式串的长度,再接下去寻找是否含有模式串,如果找不到的话,返回NULL将会退出当前循环。
AC代码二:
#include <bits/stdc++.h>
using namespace std;
char *p,text[],pattern[];int len,num;
int main(){
while(cin>>text&&strcmp(text,"#")){
cin>>pattern;
num=,len=strlen(pattern);
for(p=text;(p=strstr(p,pattern))!=NULL;num++,p+=len);
cout<<num<<endl;
}
return ;
}
题解报告:hdu 2087 剪花布条(KMP入门)的更多相关文章
- hdu 2087剪花布条 (KMP入门 子串出现的次数和子串个数)
剪花布条 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- HDU 2087 - 剪花布条 - [KMP算法]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2087 Time Limit: 1000/1000 MS (Java/Others) Memory Li ...
- HDU 2087 剪花布条 (KMP 不允许重叠的匹配)
题目链接 Problem Description 一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案.对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢? Inp ...
- HDU 2087 剪花布条(字符串匹配,KMP)
HDU 2087 剪花布条(字符串匹配,KMP) Description 一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案.对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出 ...
- HDU 2087 剪花布条 (简单KMP或者暴力)
剪花布条 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- HDU 2087 剪花布条 (字符串哈希)
http://acm.hdu.edu.cn/showproblem.php?pid=2087 Problem Description 一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图 ...
- HDU 2087 剪花布条【在字符串中不可重叠地寻找子串数量】
一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案.对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢? Input输入中含有一些数据,分别是成对出现的花布条和 ...
- HDU 2087 剪花布条 KMP入门
Problem Description 一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案.对于给定的花布条和小饰条.计算一下能从花布条中尽可能剪出几块小饰条来呢? Input ...
- HDU 2087 剪花布条(模式串在主串中出现的次数主串中子串不可重叠)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2087 题意:求模式串在主串中出现的次数,与模式串匹配的子串之间不可重叠. 思路:用kmp算法解决,在匹 ...
- HDU 2087 剪花布条 KMP极其初级之入门题(KMP模板在这里)
Problem Description 一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案.对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢? Input ...
随机推荐
- C# Json反序列化 数据协定类型 无法反序列化 由于未找到必需的数据成员
背景今天在使用:C# Json 序列化与反序列化 反序列化的时候出现了以下的错误信息. System.Runtime.Serialization.SerializationException: 数据协 ...
- Android 四大组件学习之Service五
本节学习IntentService, 可能就有人问了. 什么是IntentService, IntentService有什么作用? 不是已经有了Service,那为什么还要引入IntentServic ...
- java notify notifyAll
notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法. void notify(): 唤醒一个正在等待该对象的线程.void notifyAll(): 唤醒所 ...
- Python中解决中文乱码问题
乱码原因:因为你的文件声明为utf-8,并且也应该是用utf-8的编码保存的源文件.但是windows的本地默认编码是cp936,也就是gbk编码,所以在控制台直接打印utf-8的字符串当然是乱码了. ...
- 如何使Linux系统上的程序开机后自动运行 (转)
Linux有自己一套完整的启动体系,抓住了Linux启动的脉络,Linux的启动过程将不再神秘. 阅读之前建议先看一下附图. 本文中假设inittab中设置的init tree为: /etc/rc.d ...
- UNIX网络编程学习(9)--getsockname和getpeername的用法及实例(转)
getsockname和getpeername #include <sys/socket.h>int getsockname(int sockfd, struct sockaddr *lo ...
- sharpdevelop 调整代码字体显示大小
SharpDevelop中使用ctrl+鼠标滚轮可以调整代码的字体显示大小
- Python爬虫开发【第1篇】【动态HTML、Selenium、PhantomJS】
JavaScript JavaScript 是网络上最常用也是支持者最多的客户端脚本语言.它可以收集用户的跟踪数据,不需要重载页面直接提交表单,在页面嵌入多媒体文件,甚至运行网页游戏. 我们可以在网页 ...
- web 开发之js---ajax 异步处理
本文介绍了如何创建能够适应不同浏览器的XMLHttpRequest实例,建立和发送请求,并响应服务器.您将开始接触最基本和基础性的有关Ajax的全部对象和编程方法:XMLHttpRequest对象.该 ...
- solr 7.2.1 单机及伪集群启动
1.solr的下载: 下载地址:solr官网:http://lucene.apache.org/solr进入官网点击download或者点击链接https://lucene.apache.org/so ...