串的模式匹配——BF算法、KMP算法

字串的定位操作通常称做串的模式匹配

BF算法

朴素的模式匹配算法。
对主串的每一个字符作为字串开头,与要匹配的字符串进行匹配。对主串做大循环,每个字符开头做T的长度的小循环,直到匹配成功或全部遍历完成为止。

输出结果:

从第5开始匹配
主串s:goodgooglehhhh
字串t:    google
public class BFDemo {
    // charAt取值从0开始
    static int index(String s,String t,int pos) {
        int i = pos; //主串下标
        int j = 0; //子串下标
        // 当i小于s长度,并且j小于t长度时循环
        while (i < s.length() && j < t.length()) {
            if (s.charAt(i) == t.charAt(j)) { // 两字母相等则继续
                ++i;
                ++j;
            } else {    // 后退并重新开始匹配
                i = i -j + 1; // i退回到上次匹配首位的下一位
                j = 0;      // j退回到字串t的首位
            }
        }
        if (j+1 >= t.length())
            return i - t.length()+1;
        else
            return 0;
    }

    public static void main(String[] args) {
        String s = "goodgooglehhhh";
        String t = "google";
        System.out.println("从第"+index(s, t, 0)+"开始匹配");
        System.out.println("主串s:"+s);
        System.out.print("字串t:");
        for (int i = 0; i < index(s,t,0)-1; i++) {
            System.out.print(" ");
        }
        System.out.println(t);
    }
}

KMP算法

由于朴素模式匹配算法的低效,D.E.Knuth、J.H.Morris和V.R.Pratt三位前辈发表一个模式匹配算法,可以大大避免重复遍历的情况,我们把它称为克努特-莫里斯-普拉特算法,简称KMP算法。

核心:避免不必要的回溯。问题由模式串决定,不是由目标串决定。

我们把T串各个位置的j值的变化定义为一个数组next,那么next的长度就是T串的长度。于是可以得到下面的函数定义:
next函数

输出结果:

主串s=abmababsasb
子串t=ababs
next数组=[-1,0,0,1,2,]
从第4元素开始,主串和子串匹配
abmababsasb
   ababs
public class KMPTest {
    public static int[] getNext(String t,int[] next) {
        next[0] = -1;
        // 1)
        int j = 1,k = -1;
        while (j < t.length()) {
            if (k == -1 || t.charAt(j-1) == t.charAt(k)) {
                k++;
                next[j] = k;
            } else {
                next[j] = 0;
                k = 0;
            }
            j++;
        }
        // 2)
//        for (int j=1,k=-1; j < t.length(); j++ ) {
//            if (k == -1 || t.charAt(j-1) == t.charAt(k)) {
//                k++;
//                next[j] = k;
//            } else {
//                next[j] = 0;
//                k = 0;
//            }
//        }
        return next;
    }

    public static int indexKMP(String s,String t,int[] next) {
        int i=1; // 主串
        int j=1; // 子串
        while (i <= s.length() && j <= t.length()) { // 下标不超过串长
            // j==0,子串已回溯到头;主串与子串匹配,两个串都继续匹配下一个字符
            if (j == 0 || s.charAt(i - 1) == t.charAt(j - 1)) {
                i++;
                j++;
            } else { // 主/子串不匹配,子串回溯,根据next
                j = next[j-1]+1;
            }
        }
        if (j > t.length()-1) //
            return i - t.length();
        else
            return 0;
    }

    public static void main(String[] args) {
        String s = "abmababsasb";
        String t = "ababs";
        int[] next = new int[t.length()];
        next = getNext(t, next);
        System.out.println("主串s="+s);
        System.out.println("子串t="+t);
        System.out.print("next数组=[");
        for (int n : next) {
            System.out.print("" + n + ",");
        }
        System.out.println("]");
        System.out.println("从第"+indexKMP(s, t, next)+"元素开始,主串和子串匹配");
        System.out.println(s);
        for (int i = 0; i < (indexKMP(s,t,next)-1); i++) {
            System.out.print(" ");
        }
        System.out.println(t);
    }
}

改进版的KMP算法

类似"aaaaax"这样的模式串,如果使用上面的KMP算法,得到next数组是[0,1,2,3,4,5],则需要挨个回退比较。但是其实没有必要,因为前面5个都是a。
nextval
即,在next数组已得到的前提下,比较t[j]是否等于t[next[j]],如果相等,nextval[j]=next[next[j]];否则,nextval[j]=next[j]。

输出结果:

nextval===0 0 0 0 0 0 5 

// 改进版KMP算法
public class KMPPlusDemo {
    public static int[] get_nextval(String t,int[] nextval) {
        int i = 1,j = 0;
        nextval[1] = 0;
        while (i < t.length()) {
            if (j == 0 || t.charAt(i-1) == t.charAt(j-1)) {
                ++i;
                ++j;
                if (t.charAt(i-1) != t.charAt(j-1)) {
                    nextval[i-1] = j-1;
                } else {
                    nextval[i-1] = nextval[j-1];
                }
            } else {
                j = nextval[j-1]+1;
            }
        }
        return nextval;
    }

    public static void main(String[] args) {
        String t = "aaaaaax";
        int[] nv = new int[t.length()];
        nv = get_nextval(t,nv);
        System.out.print("nextval===");
        for (int n : nv) {
            System.out.print(n + " ");
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值