Java ———KMP算法(字符串匹配)

来源:互联网 发布:矢量软件 编辑:程序博客网 时间:2024/06/06 10:47

问题:假设有这么一类问题:存在2个字符串,其中一个是另外一个的子串。
例如:
输入s1=abcd,s2=bc,输出:1。abcd从0开始算起。
解决一:
暴力遍历解决(遍历!!!)
思路:假设S=[a,b,c,d],p=[bc],找出P在S中的位置
1.判断s[0]==p[0],显然a!=b.
2.根据i=i-j+1,j=0; 判断s[0]==p[1],b==b成立
3.i++,j++; 判断s[1]==p[2],c=c成立,此时 符合j==p.length长度
4.return回去,已找到P字符串在S中的位置
贴代码

    public static int baoli(String s[],String p[]){        int i=0,j=0;        while(i<s.length&&j<p.length){            if(s[i]==p[j]){                i++;                j++;            }            else {                i=i-j+1;                j=0;            }        }        if(j==p.length) {            return i - j;}     else return -1;    }

解法二
KMP算法
定义:Knuth-Morris-Pratt 字符串查找算法,简称为 “KMP算法”,常用于在一个文本串S内查找一个模式串P 的出现位置。
此算法可以在O(m+n)的时间数量级上完成串的模式匹配操作。其改进在于:每当一趟匹配过程中出现字符比较不等时,不需回溯i指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽肯能远的一段距离后,继续进行比较。
例:
假设两个字符串 abcac , ababc abcac bab

第一趟匹配
i=3
a b a b c a b c a c b a b
a b c
j=3
第二躺匹配
i=3->i=7
a b a b c a b c a c b a b
a b c a c
j=1->j=5, 到第5个字符不匹配
第三趟匹配
i=7->i=11
a b a b c a b c a c b a b
(a)b c a c
j=1->j=6,5个字符匹配完成

通过对上述观察,我们可以看到i=4和j=1,i=5和j=1以及i=6,j=1这三次比较都是不必进行的,因为从第三趟结果就可以知道主串中4,5,6必然是bca,(即模式中的2,3,4)。因为模式中的第一个字符为a,因此它无需再和这三个字符进行比较,而仅需将主串中的i=4移到i=7,用i=7与j=2进行比较即可。
同理,在第一趟匹配出现字符不等时,继续将i向又移动两个字符的位置继续进行i=3,j=1的字符比较。
结论:失配时
模式串(针对整个模式串)向右移动的位数=已匹配字符数 - 失配字符的上一位字符所对应的最大长度值
得知最大长度表变可求next数组
所以我们要知道最大长度表怎么算。(以下例子很形象)
这里写图片描述
next 数组相当于“最大长度值” 整体向右移动一位,然后初始值赋为-1。就是找最大对称长度的前缀后缀,然后整体右移一位,初值赋为-1
然后便可用next数组来改变刚刚的公式
则失配时,
模式串向右移动的位数为=失配字符所在位置 - 失配字符对应的next 值
看了大致介绍,先贴代码根据代码讲解

public class KMPSearch {    public static int kmpsearch(String s,String key,int[] next){          int i=0;          int j=0;     while (i<s.length()&&j<key.length()){         //如果j=-1或者当前字符串匹配陈功(即s[i]==key[j]),都令i++.j++         if(j==-1||s.charAt(i)==key.charAt(j)){         i++;         j++;         }         else {             //如果当前j!=-1,且当前字符匹配失败(即s[i]!=key[j]),则令i不变,j=next[j]             //next[j]即为j对应的next值             j=next[j];         }     }     if(j==key.length()){         return i-j;     }     else return 0;    }    public static int[] kmpnext(String key){        int next[]= new int[key.length()];        next[0]=-1;        int k=-1;        int j=0;       while (j<key.length()-1){           if(k==-1){               next[++j]=0;               k=0;           }           else if(key.charAt(j) == key.charAt(k)){//确定next(j+1)的值        k++;        next[++j]=k;           }           else k=next[k];       }        return next;    }    public static void main(String args[]){        String str1="sdffsefdbababa";        String key="ababa";        int[] next=kmpnext(key);        int res=kmpsearch(str1,key,next);        System.out.println(res);}}

可从分析定义用递推的方法求next函数值
定义next[0]=-1;
设next[j]=k,则表明以下关系’p1……pk-1’=’pj-k+1……pj-1’
则求next[j+1]=?肯能有两种情况:
1.若pk=pj,则表明 ‘p1……pk’=’pj-k+1……pj’
next[j+1]=k+1=next[j]+1
2.若pk!=pj,则表明’p1……pk’!=’pj-k+1……pj’
则next[j+1]=k’+1
则next[j+1]=next[k]+1
所以得到next[j+1]=1.
所以总结来说next[0]=-1,比较k,j。
若S[k]=S[j],则next[j+1]=k+1.两者+1
若不相等则k[next]=k,对k重新赋值再比较,直到j循环结束。

参考:http://blog.csdn.net/christ1750/article/details/51259425
http://blog.csdn.net/v_july_v/article/details/7041827
http://blog.csdn.net/lzy969737017/article/details/38088865

原创粉丝点击