字符串匹配的问题应该大家经常遇到的,那么同样,我也遇到了这样的一些问题,也有一些心得和大家分享一二。
我们假设一个字符串匹配的很一般的背景:已知S、T两个字符串,其中S为原字符串,T为需要我们匹配的字符串,主要任务就是完成到S中匹配T字符串。如果能够匹配到的话返回对应的字符串的位置。
我们假设原字符串长度为M,字串的长度为N。
1. 首先我们先看朴素匹配的方法:
主要内容:原理不多做介绍,本质而言就是从原字符串开始搜索,如果出现不能匹配,原搜索位置后移一位(我们默认搜索的方向是从前往后,从左往右)。
附:java实现
public static int BFSearch(String original,String find)
{
int len1=original.length();
int len2=find.length();
int j=0;
int i=0;
while(j<len2 )
{
if(original.charAt(i)!=find.charAt(j))
i++;
else
{
i++;
j++;
}
}
return i-j;
}
2.
在这个基础上面我们考虑能不能对算法做些优化呢?如果优化的话应该在哪里优化呢?
我们下面来分析上面的过程,上面的字符串匹配的过程中,我们会注意到每次匹配尝试失败之后需要回溯,也就是需要从匹配的原字符串的位置往后移动一位考虑,那么在某些情况下,这样的操作会不会明显有问题呢?
答案是某些情况下面我们可以进行优化。
在这里 感谢其他网友的示例图片
http://www.ituring.com.cn/article/59881
通过上面的例子我们可以发现,我们在匹配的时候,最后的一位不合适的时候其实我们可以直接从第五个位置开始匹配,为什么呢?
因为abcde的字符串第一个到第四个各不相同,第五个没有和原字符串匹配成功,意味着什么?意味着前面的四个是匹配的,那么当我把字符串匹配的初始位置向后移动一位、两位、三位、四位肯定是不匹配的。相信看到这里你们已经抓住了问题的关键:分析目标字符串本身的性质。换言之,用一句比价朴素的话来讲就是,通过分析目标字符串的相似部分的情况,然后在回溯的时候减少回溯的步数,更少的回溯意味着更高的效率。
kmp 算法的核心即是计算字符串f每一个位置之前的字符串的前缀和后缀公共部分的最大长度。获得f每一个位置的最大公共长度之后,就可以利用该最大公共长度快速和字符串O比较。当每次比较到两个字符串的字符不同时,我们就可以根据最大公共长度将字符串f向前移动(已匹配长度-最大公共长度)位,接着继续比较下一个位置。事实上,字符串f的前移只是概念上的前移,只要我们在比较的时候从最大公共长度之后比较f和O即可达到字符串f前移的目的。
在这里引入一个示例来说明这个问题,示例中一个重要的方面是创建对应的next数组,示例中对于前缀和后缀对于next数组的产生有着重要的作用。
示例地址:
http://blog.csdn.net/buaa_shang/article/details/9907183
作者:阮一峰
http://www.ruanyifeng.com/home.html
博客地址:
http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html
通过上面的一个示例,相信大家对于整个KMP算法如何计算应该有了比较详细的了解。我就不通过语言进行多余的赘述了,我个人更喜欢通过图形化的方式记下相关的算法的原理,最后附上相关的代码:
public static void Search(String original,String find,int next[])
{
int j=0;
for(int i=0;i<original.length();i++)
{
while(j>0&&original.charAt(i)!=find.charAt(j))
j=next[j];
if(original.charAt(i)==find.charAt(j))
j++;
if(j==find.length())
{
System.out.println("find at pos "+(i-j+1));
System.out.println(original.subSequence(i-j+1, i+1));
j=next[j];
}
}
}
public static int[] getNext(String b)
{
int len=b.length();
int j=0;
int next[]=new int [len+1];next 表示长度为i的字符串的前缀和后缀的最长公共部分 从1开始
next[0]=next[1]=0;
for(int i=1;i<len;i++)
{
while(j>0&&b.charAt(i)!=b.charAt(j))
j=next[j];
if(b.charAt(i)==b.charAt(j))
j++;
next[i+1]=j;
}
return next;
}
public static int BFSearch(String original,String find)
{
int len1=original.length();
int len2=find.length();
int j=0;
int i=0;
while(j<len2 )
{
if(original.charAt(i)!=find.charAt(j))
i++;
else
{
i++;
j++;
}
}
return i-j;
}
如有问题 欢迎指正 欢迎交流。
最后还有相关的博客作为个人整理的资料的参考,提供给大家,非常感谢下面的博客的博主带来的明细的讲解。
1. http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html2 http://www.ituring.com.cn/article/59881
3. http://blog.csdn.net/yutianzuijin/article/details/11954939
4. http://blog.csdn.net/WINCOL/article/details/4795369