hihoCoder 1015 KMP算法(kmp)
2016-04-16 12:03
246 查看
#1015 : KMP算法
时间限制:1000ms单点时限:1000ms
内存限制:256MB
描述
小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进。这一天,他们遇到了一只河蟹,于是河蟹就向小Hi和小Ho提出了那个经典的问题:“小Hi和小Ho,你们能不能够判断一段文字(原串)里面是不是存在那么一些……特殊……的文字(模式串)?”
小Hi和小Ho仔细思考了一下,觉得只能想到很简单的做法,但是又觉得既然河蟹先生这么说了,就肯定不会这么容易的让他们回答了,于是他们只能说道:“抱歉,河蟹先生,我们只能想到时间复杂度为(文本长度 * 特殊文字总长度)的方法,即对于每个模式串分开判断,然后依次枚举起始位置并检查是否能够匹配,但是这不是您想要的方法是吧?”
河蟹点了点头,说道:”看来你们的水平还有待提高,这样吧,如果我说只有一个特殊文字,你能不能做到呢?“
小Ho这时候还有点晕晕乎乎的,但是小Hi很快开口道:”我知道!这就是一个很经典的模式匹配问题!可以使用KMP算法进行求解!“
河蟹满意的点了点头,对小Hi说道:”既然你知道就好办了,你去把小Ho教会,下周我有重要的任务交给你们!“
”保证完成任务!”小Hi点头道。
提示一:KMP的思路
提示二:NEXT数组的使用
提示三:如何求解NEXT数组
输入
第一行一个整数N,表示测试数据组数。接下来的N*2行,每两行表示一个测试数据。在每一个测试数据中,第一行为模式串,由不超过10^4个大写字母组成,第二行为原串,由不超过10^6个大写字母组成。
其中N<=20
输出
对于每一个测试数据,按照它们在输入中出现的顺序输出一行Ans,表示模式串在原串中出现的次数。×Close
提示一:KMP的思路
小Hi和小Ho回到了学校,为了完成河蟹托付的伟大使命,小Hi立马把小Ho抓到了机房开始上课。“小Ho,你来看看这样一段原串和模式串~”小Hi说着递上了一张纸条。
原串: | bababababababababb |
模式串: | bababb |
“我们假设仍然使用最普通的方法来进行判断,即我们先枚举原串中的一个起始位置,然后判断从这个位置开始的字符串是否能和模式串进行完匹配。”小HI说道,“然后我们来看这个过程中有没有什么可以缩减的计算量。”
“好的!”小Ho点点头。
“你看,在起始点为1的时候,匹配到第6个字符的时候发生了失败,这个时候我们应当做的是是不是将模式串右移一位,然后从头开始判断,就像这样?”小Hi又在纸上画了画,递给了小Ho。“
原串: | bababababababababb |
模式串: | bababb |
原串: | bababababababababb |
模式串: | bababb |
”然后我们再将模式串右移一位,然后再从头开始判断,这次我们成功的越过了原串的第7个字符,在第8个字符产生了不同。“小Hi继续往下推演。
原串: | bababababababababb |
模式串: | bababb |
”那你觉得这个过程中有没有什么没有必要计算的呢?“小Hi于是问道。
”我是这么认为的,你看这条线。“小Ho在两个串上对着的一个位置画了一条线。
原串: | babab | ababababababb |
模式串: | babab | b |
“这是我们第一次产生了字符不匹配的情况,那么接下来的过程中一定会出现两种情况之一:一种情况是模式串与原串的对齐点(即枚举的原串中的起点位置)越过了这条线,仍然没能匹配成功,而另一种情况是原串中这个位置的字符与模式串中某个位置的字符匹配上了。”小Ho分析道:”我们先不考虑第一种情况,而来看看第二种情况会发生什么。“
原串: | babab | ababababababb |
模式串(对齐点=1): | babab | b |
模式串(对齐点=3): | bab | a |
“那当然,毕竟我最近在讨论区解答了很多问题,这很锻炼人的好么!“小Ho笑嘻嘻的回答道。
”那我也得表现下,接下来换我来说吧,反正你肯定也就差不多想到这么多是吧!“小Hi也是看破了小Ho的底细,这般说道。于是小Ho点了点头,让小Hi接着说。
”我相信一个很容易注意到的事实就在于,如果我用i表示原串和模式串产生分歧的位置(模式串上的位置,注意!这个和对齐点是不一样的东西,一个在原串上,一个在模式串上),用j表示为了匹配掉位置i上产生分歧的字符而将模式串的对齐点移动到的位置,我们会发现,模式串[1, i-j]的这一段和[j, i - 1]这一段是相同的。比如在这个例子中i=6,j=3,我们会发现模式串[1, 3]和[3,5]是相同的。“小Hi整理了下思路,如是说道。
原串: | ba | bab | a babababababb |
模式串(i=1): | ba | bab | b |
模式串(i=3): | | bab | a |
“我已经开始有点晕了!”小Ho提出了抗议。
“那你就好好的读一遍我刚才说的话!然后自己在草稿纸上演算一下这个样例,很快就可以得出结果的!”小Hi如是说道。”总而言之我们现在需要的一个数据是,这个长度k最长是多少,而且我们对于模式串的每一个位置i,都要计算这个值。”而这就是KMP中最为重要的一个点——NEXT数组。
Close
×Close
提示二:NEXT数组的使用
“那么,为了能够充分理解NEXT数组,我们再来回顾一下如何使用NEXT数组~"小Hi摆出一副老师的样子,说道。”首先我们来给出NEXT数组的数学定义~“NEXT[0] = -1 NEXT[i] = max{ 0<=k< i | str.substring(1, k) == str.substring(i - k +1 , i) } 其中str.substring(i, j)表示str从位置i到位置j的子串,如果i>j则,substring为空
”那么我们对之前例子中的模式串进行求解,可以得到这样的NEXT数组。“小Hi在纸上写了又写,画了又画。
模式串: | b a b a b b |
NEXT: | 0 0 1 2 3 1 |
”好的!小Hi老师好棒!“小Ho在一旁煽风点火道。
原串(p=5): | babab | abcbababababb |
模式串(q=5): | babab | b |
原串(p=5): | babab | abcbababababb |
模式串(q=3): | bab | abb |
原串(p=7): | bababab | cbababababb |
模式串(q=5): | babab | b |
原串(p=7): | bababab | cbababababb |
模式串(q=3): | bab | abb |
原串(p=7): | bababab | cbababababb |
模式串(q=1): | b | ababb |
原串(p=7): | bababab | cbababababb |
模式串(q=0): | | bababb |
原串(p=7): | bababab | cbababababb |
模式串(q=-1): | | bababb |
“这样一说,我就大致能够理解NEXT数组是怎么用来求解模式匹配问题的了,但是它是如何求的呢?一般的方法不是要O(模式串长度的立方)的么?”小Ho问道。
“这就是我接下来要和你说的啦!”小Hi笑道:“但是让我先喝口水!”
Close
×Close
提示三:如何求解NEXT数组
“首先我们不想如何求整个NEXT数组,而是假设我们已经知道了之前例子中模式串的NEXT[1..4],来求NEXT[5]如何?”小Hi建议道。“好的!这样我们就只需要平方级的算法就可以算出它的值了!”小Ho高兴道。
“有点追求好不好!”小Hi深深的吸了一口气:“你这样和之前的解法有什么不同么!”
“似乎没有。。那你说怎么算吧!我反正脑子已经成浆糊了。”小Ho郁闷道。
“我们把par.substring(1, 5)当做新的原串ori_new,然后把par.substring(1, 4)当做新的模式串par,会如何?”小Hi微微一笑。
“会。。我来试试!"小Ho接过小Hi手中的纸笔,便开始演算:“首先就直接匹配到了p=4, q=4的情况,这时候严格来说已经算匹配完成了,但是肯定不是就这么结束的,此时par_new[q +1]因为是空字符,所以肯定和ori_new[p+1]匹配不上。于是令q = NEXT[q]”
原串(p=4): | baba | b |
模式串(q=4): | baba | |
原串(p=4): | baba | b |
模式串(q=2): | ba | b |
”没错,就是这样!那你想想现在如何求NEXT[6]。“小Hi继续引导小Ho。
”首先我们没有必要重新从头开始匹配,直接在原串和模式串的后面加上第6个字符就可以了。“小Ho分析道。
原串(p=5): | babab | b |
模式串(q=3): | bab | abb |
原串(p=5): | babab | b |
模式串(q=1): | b | ababb |
原串(p=5): | babab | b |
模式串(q=0): | | bababb |
”那你要不要赶紧去写一下代码,KMP算法的代码可是可以写的很短很巧妙的哦!~“小Hi建议道。
”好!“
Close
样例输入
5 HA HAHAHA WQN WQN ADA ADADADA BABABB BABABABABABABABABB DAD ADDAADAADDAAADAAD
样例输出
3 1 3 1 0
没什么好说的,模板题
#include<cstdio> #include<cstring> #include<algorithm> #include <iostream> #include <string> using namespace std; int f[ 15000]; void getfill(string s) { memset(f,0,sizeof(f)); //根据其前一个字母得到 for(int i=1;i<s.size();i++) { int j=f[i]; while(j && s[i]!=s[j]) j=f[j]; f[i+1]=(s[i]==s[j])?j+1:0; } } int find(string a,string s) { int ans=0; getfill(s);int j=0; for(int i=0;i<a.size();i++) { while(j && a[i]!=s[j]) j=f[j]; if(a[i]==s[j]) j++; if(j==s.size()){ ans++; } } return ans; } int main() { string s,a; int T; scanf("%d",&T); while(T--) { getchar(); cin>>s>>a; int ans=find(a,s); printf("%d\n",ans); } return 0; }
相关文章推荐
- Fragment页面的切换
- JAVA修改组件
- 【模拟】HDU4509湫湫系列故事——减肥记II
- Windows Docker的有趣事实 推荐
- Linux用户管理:addgroup、adduser、usermod
- android 调用远程service时报java.lang.SecurityException: Binder invocation to an incorrect interface异常
- position、opacity与z-index的组合
- 复利计算再升级
- 关于stm32定时器的理解
- SeimiAgent——基于QtWebkit的通用网页渲染代理服务
- 关于stm32定时器的理解
- Android 学习记录-零散知识点
- 11. Container With Most Water
- jQuery实现漂亮实用的商品图片tips提示框效果(无图片箭头+阴影)
- 什么是P问题、NP问题和NPC问题
- spring mvc中的@PathVariable
- CentOS安装软件<一> MYSQL
- c++(1)
- 单例模式的五种写法
- 织梦后台jpeg格式图片没法上传,图片浏览器中没法查看预览