KMP算法(【028-Implement strStr() (实现strStr()函数)】)

package algorithm;

/*
 * KMP算法
 * 字符串和模式串的匹配问题
 设主串(下文中我们称作S)为:BBC ABCDAB ABCDABCDABDE
 模式串(下文中我们称作P)为:ABCDABD
 * 1、暴力算法匹配
 * 如果s[i]!=p[j],则回溯 p[0],s则为i=i-j+1,移动一位。
 * 
 * 2、KMP算法
 i为遍历S,j为遍历P
 1、首先s[0],s[1],s[2],s[3]与p[0]均不匹配。则直接为:
 S:BBC ABCDAB ABCDABCDABDE
 P:    ABCDABD

 2、S[4]=A 与 P[0]匹配,一直到S[10]与P[6]不匹配。暴力匹配为i=5,则j=0,重新匹配。此时S[5]=B 与P[0]=A 不匹配,
 S:BBC ABCDAB ABCDABCDABDE
 P:     ABCDABD

 以前S[5]=P[1],而P[1]!=P[0],故一定不匹配。利用以前匹配过的来匹配当前的。

 3 、保持i不变,如何让j改变,去动态适应呢。

 KMP算法的定义:
 (1)如果字符串匹配,即S[i]==P[j],则i++,j++,继续匹配下一个字符
 (2)如果字符串匹配不成功,即S[i]!=P[j],则i改变,j=next[j],此时j向右移动了j-next[j]位置。

 S:BBC ABCDAB ABCDABCDABDE
 P:    ABCDABD
 next[6]=2,即i=10不变,j=next[6]=P[2],即S[10]与P[2]匹配

 next[6]=2,此为j=6,此为D,前面为ABCDAB,此时ABCDAB中长度最大且相等的前缀和后缀,此为AB,只有此时模式串向右移动时,
 正好与S匹配,因前面匹配,即S[9]=P[5]=p[1]=B,S[8]=P[4]=P[0]=A,

 S:BBC ABCDAB ABCDABCDABDE
 P:        ABCDABD

 发现S[10]!=P[2],则next[2],此时C前为AB,则next[2]=0

 S:BBC ABCDAB ABCDABCDABDE
 P:          ABCDABD

 此时由于S[10]!=P[0],则应该S[11]与P[0]比较
 S:BBC ABCDAB ABCDABCDABDE
 P:           ABCDABD

 发现S[17]=C与P[6]=D 不匹配,此时i=17,next[6]=2,j=6-2向右移动4位,则如下, 此后一直匹配,则成功。
 S:BBC ABCDAB ABCDABCDABDE
 P:               ABCDABD

 此时算法为  getKMPBeginPosition1; 
 (*i=0 j=0时,如果不匹配,则 next[j]=next[0],此时初始化next[0]=-1,此时i和j均需向前移动,即i++,和j++***)

 * 
 * 
 * */
public class KMP {

    public static int violenceMatch(String s, String p) {
        if (s == null || p == null) {
            return -1;
        }
        if (p.length() > s.length()) {
            return -1;
        }
        int sLength = s.length();
        int pLength = p.length();
        int i = 0, j = 0;
        while (i < sLength && j < pLength) {
            char s1 = s.charAt(i);
            char p1 = p.charAt(j);
            if (s1 == p1) {
                i++;
                j++;
            } else {
                i = i - j + 1;
                j = 0;
            }
        }
        if (j == pLength) {
            return i - j;
        }
        return -1;
    }

    /*
     * 
     * */

    public int getKMPBeginPosition1(String S, String P) {

        if (S == null || P == null) {
            return -1;
        }
        if (P.length() > S.length()) {
            return -1;
        }
        int sLength = S.length();
        int pLength = P.length();

        int[] next = getNextValue(P);
        int i = 0, j = 0;
        while (i < sLength && j < pLength) {

            if (j == -1||S.charAt(i) == P.charAt(j)) {
                i++;
                j++;
            } else { // 如果不匹配,则i不动,移动j,使j=next[j]
                j = next[j];
            }
        }
        if (j == pLength) {
            return i - j;
        }
        return -1;
    }

    /*
     * 求next[j],当S[i]!=P[j]时,i保持不变,只移动j,next[j]为j前面 前缀后缀最长公共元素长度 如 S:BBC ABCDAB
     * ABCDABCDABDE P: ABCDABD 当S[10]与P[6]不匹配时,需要找到P[6]前 ABCDAB的前缀后缀最长公共元素长度,此时为
     * AB,此为2,此时next[6]=2 (0,1,2)
     * 
     * 
     * 已知:next[j]=k,求next[k+1]的值,即 在前移一个字符时,此时需比较:p[k]与p[j]
     * (1)如果p[k]==p[j],则next[j+1]=next[j]+1=k+1
     * (2)如果p[k]!=p[j],此时需要比较p[next[k]]与p[j],即j不变,k=next[k], 如果p[j]==p[next[k]],
     * 则next[j+1]=next[k]+1, 一直递归,直到相同。
     * 
     * 求next[0-6], p[0]=A,p[1]=B,p[2]=C,p[3]=D,p[4]=A,p[5]=B,p[6]=D
     * j=0,next[0]=-1 j=1,即next[1]=0; j+1=2 ,j=1,k=0, p[0]!=p[1],递归
     * j=1,k=next[k]=-1 ,则直接结束 next[2]=next[0]+1=0
     * 
     * j+1=3,j=2,k=next[2]=0, 此时p[2]!=p[0], 继续递归,k=next[0]=-1,
     * 则结束为next[3]=next[0]+1=0
     * 
     * j+1=4,j=3,k=next[3]=0,则p[3]!=p[0],继续递归,k=next[0]=-1,则next[4]=-1+1=0
     * j+1=5,j=4,k=next[4]=0,则p[4]==p[0],无需递归,next[5]=next[4]+1=1
     * 
     * j+1=6,j=5,k=next[5]=1,则p[5]==p[1],则next[6]=next[5]+1=2
     * j+1=7,j=6,k=next[6]
     * =2,则p[6]!=p[2],递归k=next[2]=0,则p[6]!=p[0],k=next[0]=-1,则next
     * [7]=next[0]+1=0
     */
    //易出错的地方为:  P.charAt(k) == P.charAt(j)
    public int[] getNextValue(String P) {

        if (P == null) {
            return null;
        }

        int plength = P.length();
        int k = -1;
        int[] next = new int[plength];
        next[0] = -1;
        int j = 0;

        while (j < plength - 1) {
            if (k == -1 || P.charAt(k) == P.charAt(j)) {  //
                k++;
                j++;
                next[j] = k;
            } else {
                k = next[k];
            }
        }

        return next;
    }

    public void printP(String p, int[] pints) {
        for (int i = 0; i < p.length(); i++) {
            System.out.println(p.charAt(i) + "->" + pints[i]);
        }
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        KMP kmp = new KMP();
        String s = "BBC ABCDAB ABCDABCDABDE";
        String p = "ABCDABD";

        //int[] pints = kmp.getNextValue(p);
        //kmp.printP(p, pints);

         int result=violenceMatch(s,p);
         int r=kmp.getKMPBeginPosition1(s,p);
         System.out.println("暴力破解算法,序号为:"+result+"  ,直接indexOf算法为:"+s.indexOf(p));
         System.out.println("KMP算法为,序号为:"+r);

    }

}
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值