KMP算法代学习之(二)代码深入学习
2012-11-08 14:29
288 查看
前段时间学习了KMP算法,感觉略懂略懂!但算法这个东西理解一定要深入,不是略懂略懂就能敷衍过去的!真真做题的时候,才发现仅仅套模板是根本没用的,必须自己写得来代码,这样的话才能从细节,从根本上了解KMP算法的实现过程!
既然这样的话,就只好再把KMP算法弄出来好好学习一翻了!
很简单的一个例子:有I am xiao shuai.这一个句子,问“xiao shuai”在不在这说话中,如果在,起始位置在哪里,如果不在,返回-1。很显然xiao shuai是在这个句子中的,而且起始位置是5。
这个问题怎么用算法来解决呢,只是单纯地解决问题,不一定用KMP算法?
这是最朴素的字符串匹配算法。但是呢,这个实现不怎么优雅,我们把代码修改一下:
这样看要简洁多了! 其实这种匹配思想是很明白的,在匹配的过程中,如果出现不匹配的情况:那么i=i-j+1,j=0,即i回溯到本轮不匹配时起始位置的下一个位置,j回溯到自己开头!
这样解决问题是可行的,但其执行的效率呢?如果字符串的长度达到100w级别,依靠这个算法能快速解决问题吗?
我们再回到刚才的例子,我们真的有必要在不匹配的时候,把j老老实实地退回去,让i只是从本轮匹配开始的地方往前一步吗? answer is No!
这时候我们才真真转入KMP算法的学习。KMP算法就是研究字符串在匹配的过程中中途怎么跳的问题!即怎么能跳得快一点,而不是傻傻地把j老老实实地退回去,让i只是从本轮匹配开始的地方往前一步。
KMP算法把重心专注在了匹配串,我猜想因为一般而言,匹配串的长度比原串要短很多,吃柿子的时候要挑软柿子捏嘛!认准了对象,接下来怎么做呢?
我们知道,孔子曾经说过:吾有知乎哉,无知也,有鄙夫问于我,空空如也,我叩其两端而竭焉!字符串问题的处理也遵循这种思想,叩其两端,哪两端呢? 一端是前缀,一端是后缀!比如:Trie树,后缀树!KMP算法也没有跳出这个圈圈,因为它是从前缀入手的!
比如字符串:ababacb,它的前缀有a,ab,aba,abab,ababa,ababac,ababacb。KMP算法构造了一个next数组来存储这些前缀的一种性质:不为自身的最大首尾重复子串长度。
通俗地说,就是自我覆盖程度!那么对于前缀a,其next值规定为-1,即没有自我覆盖。这一点刚开始很难想通,a本身不是自我覆盖吗?那么请看红色的文字!
一般学习KMP算法对一个长度不太大的字符串,应该有能力手工计算出其next值!
这里我们用程序来计算就可以了!
至于其计算的规则,我在第一篇KMP的博文中已经写了,也可以参考July的博客(百度:July,算法之道)!
那么,next数组计算出来后,怎么用呢?
如果比较上面的普通匹配的代码,就会发现改的东西不多!
既然这样的话,就只好再把KMP算法弄出来好好学习一翻了!
很简单的一个例子:有I am xiao shuai.这一个句子,问“xiao shuai”在不在这说话中,如果在,起始位置在哪里,如果不在,返回-1。很显然xiao shuai是在这个句子中的,而且起始位置是5。
这个问题怎么用算法来解决呢,只是单纯地解决问题,不一定用KMP算法?
int compare(char *t,char *p){ int t_len=strlen(t),p_len=strlen(p); int i,j,idx; if(t_len<p_len)return -1; for(j=idx=i=0;i<t_len;i++){ while(idx<t_len&&j<p_len&&t[idx]==p[j])idx++,j++; if(j==p_len)//表示已经匹配到了 return i; else if(idx==t_len)//已经到末尾没有匹配到 return -1; else//还没有匹配完,重新开始匹配 j=0;idx=i+1; } return -1; }
这是最朴素的字符串匹配算法。但是呢,这个实现不怎么优雅,我们把代码修改一下:
int compare(char *t,char *p){ int t_len=strlen(t),p_len=strlen(p); int i,j; if(t_len<p_len)return -1; i=j=0; while(i<t_len&&j<p_len){ if(t[i]==p[j]) i++,j++; else i=i-j+1,j=0; } if(j==p_len) return i-j; return -1; }
这样看要简洁多了! 其实这种匹配思想是很明白的,在匹配的过程中,如果出现不匹配的情况:那么i=i-j+1,j=0,即i回溯到本轮不匹配时起始位置的下一个位置,j回溯到自己开头!
这样解决问题是可行的,但其执行的效率呢?如果字符串的长度达到100w级别,依靠这个算法能快速解决问题吗?
我们再回到刚才的例子,我们真的有必要在不匹配的时候,把j老老实实地退回去,让i只是从本轮匹配开始的地方往前一步吗? answer is No!
这时候我们才真真转入KMP算法的学习。KMP算法就是研究字符串在匹配的过程中中途怎么跳的问题!即怎么能跳得快一点,而不是傻傻地把j老老实实地退回去,让i只是从本轮匹配开始的地方往前一步。
KMP算法把重心专注在了匹配串,我猜想因为一般而言,匹配串的长度比原串要短很多,吃柿子的时候要挑软柿子捏嘛!认准了对象,接下来怎么做呢?
我们知道,孔子曾经说过:吾有知乎哉,无知也,有鄙夫问于我,空空如也,我叩其两端而竭焉!字符串问题的处理也遵循这种思想,叩其两端,哪两端呢? 一端是前缀,一端是后缀!比如:Trie树,后缀树!KMP算法也没有跳出这个圈圈,因为它是从前缀入手的!
比如字符串:ababacb,它的前缀有a,ab,aba,abab,ababa,ababac,ababacb。KMP算法构造了一个next数组来存储这些前缀的一种性质:不为自身的最大首尾重复子串长度。
通俗地说,就是自我覆盖程度!那么对于前缀a,其next值规定为-1,即没有自我覆盖。这一点刚开始很难想通,a本身不是自我覆盖吗?那么请看红色的文字!
一般学习KMP算法对一个长度不太大的字符串,应该有能力手工计算出其next值!
这里我们用程序来计算就可以了!
void cal_next(char *p){ //这里计算next,主要考虑的是index的变化 int len=strlen(p); int i,index; next[0]=-1;//KMP的起点从-1开始 for(i=1;i<len;i++){ index=next[i-1];//这里有点回溯的感觉 while(index>-1&&p[index+1]!=p[i]){ index=next[index]; } if(p[index+1]==p[i]){ next[i]=index+1; }else{ next[i]=-1; } } }
至于其计算的规则,我在第一篇KMP的博文中已经写了,也可以参考July的博客(百度:July,算法之道)!
那么,next数组计算出来后,怎么用呢?
int compare(char *t,char *p){ int t_len=strlen(t),p_len=strlen(p); int i,j; if(t_len<p_len)return -1; i=j=0; cal_next(p);//与普通匹配相比,多了计算next数组的一行代码 while(i<t_len&&j<p_len){ if(t[i]==p[j]) i++,j++; //这里j=0的时候是个特殊情况,所以需要单独处理 //比如,对于字符串 t="abbbbaccababca",p="bbba" //如果没有j==0的判定,就会出现死循环 else if(j==0) i++; else j=next[j-1]+1;//这里 i不再回退了,而随着i不再回退,j也不归零了! } if(j==p_len) return i-j; return -1; }
如果比较上面的普通匹配的代码,就会发现改的东西不多!
相关文章推荐
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-4.Action Bar)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-7.通知)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-6.权限(Permissions))
- js深入学习-回调函数之代码复用
- 深入理解javascript学习笔记(一) 编写高质量代码
- 【代码笔记】XML深入学习:DTD约束与DTD语法(1)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-7.App Widgets)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-8.AndroidManifest.xml文件)
- 从零开始--系统深入学习android(实践-让我们开始写代码-指南-1.Hello,World)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-5.设置(Settings))
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-5.设置(Settings))
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-6.对话框)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-6.对话框)
- JVM深入学习-Java代码执行篇二-[装载Class ]
- 深入学习Java中的字符串,代码点和代码单元
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-2. 输入控件)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-4.Action Bar)
- 转:深入学习Java中的字符串,代码点和代码单元
- 代码之髓-第一章-如何深入高效学习地学习语言
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-3. 菜单)