zoukankan      html  css  js  c++  java
  • KMP算法

    KMP算法

    一个专门匹配字符串的算法

    e,g

    先给出一个 待匹配字符串 B

    a	b	a	a	b	b	a	b	a	a	b
    0	0	1	1	2	0	1	2	3	4	5
    
    // 从 2 个字母开始进行匹配,第一个的F数组值,默认为 0,m 即字符串长度
    for(int i = 1; i < m; i++)
    {
        // j 可以代表将与 B[i] 进行比较的 下标
        int j = F[i - 1]
        // 当 不匹配 且 待匹配下标对应的数字的 F数组值还大于 1的情况下,进行 j 的前进
        // 直到 B[i] 与 B[j] 相等 或者 到了 无对称字母位置
        // 例如 aba 中的 b处 此时 j = 0,而j = 0 对应的就是 首字母a,相当于重新开始
        while(B[i] != B[j] && j >= 1)
        {
            j = F[j - 1];
        }
        
        // 如果进一步的还继续匹配
        if(B[i] == B[j])
            F[i] = j + 1;   // 下标数组 + 1
        else
            // 否则 归零
            F[i] = 0;
        
    }
    

    利用 F数组寻找匹配,每找到一个匹配就输出其开始的位置

    // i 是对应 匹配字符串 A 中的 下标,前进
    while(i < n)
    {
        // 如果 相匹配
        if(A[i] == B[j])
        {
            i++;
            j++;
            // j 到头了,代表相匹配了,此段
            if(j == m)
            {
                // 如果输出位置是从 1开始标号的
                printf("%d
    ", i - m + 1);
                // 如果输出位置是从 0 开始标号的
                printf("%d
    ", i - m);
                // 这里的 j 再赋值的意义 自己猜吧,提示: A串可能有多个 B
                j = F[j - 1] + 1;
            }
        }
        else{
            // j == 0 代表B串开头
            if(j == 0)
                i++;
            else
                j = F[j - 1];
        }
    }
    
    

    利用上面的公式,再来个匹配字符串 A

    a	b	a	a	b	a	a	b	b	a	b	a	a	a	b	a	a	b	b	a	b	a	a b
    

    B

    a	b	a	a	b	b	a	b	a	a	b
    0	0	1	1	2	0	1	2	3	4	5
    

    第一次停

    a b a a b a
    a b a a b b
    此时 i = 5 j = 5,而 j != 1,所以 j = F[j - 1]= F[4]= 2
    继续 i = 5 j = 2, 两者相等 .... 直到
    

    第二次停

    a	b	a	a	b	b	a	b	a	a	a
    a	b	a	a	b	b	a	b	a	a	b
    此时 i = 13, j = 10, j != 1,所以 j = F[j - 1]= F[9] = 4
    还是不等, j = F[j - 1] = F[3] = 1,
    相等,然后继续,而为什么每次都这样进行 更替呢,原因是 这样来进行前缀对称处后一个与 A 串对应处比较,然后不断缩小。
    好比, a b a b c 与 a b c 进行匹配
    a b c
    0 0 0
    a b
    a b
    a b a
    a b c
    此时 j = F[3] = 0
    发现此时, j == 0,这已经是时 B串的首字母了,先判断是否相等,发现不相等,就将 A串进位了,因为A串此时位置以前的已经None了,然后 j == 0 与 i + 1 再开始循环
    ## 抛开这个例子,发现 B[1] != A[13],不得不继续降低, j = F[1] = 0(首字母处了)
    然后发现相等,继续循环比较
    a	b	a	a	b	b	a	b	a	a	b
    a	b	a	a	b	b	a	b	a	a	b
    发现相等,此时
    输出的为 i - m = 24 - 10 = 14
    j 再 归到 0 位置?,当然是可以利用前一部分再进行匹配的位置, 然后继续
    

    完整版

    package com.company;
    
    import java.util.Arrays;
    
    public class KMP {
        public static int [] Next(String str)
        {
            int [] next = new int[str.length()];
            // 将 str 转换成 字母数组
            char B[] = str.toCharArray();
            // 首先,默认第一个待匹配字母的 next 值为 0
            next[0] = 0;
            int j;
            for (int i = 1; i < str.length(); i++)
            {
                // 每一个比较下标都是前一个数的 next 值
                j = next[i - 1];
    //            System.out.println(j);
                // 进行判断,如果 B[i] 与 B[j] 不等,且前面 next[i - 1] >= 1,即前一个字母有前缀匹配项时
                // j = next[j]
                // 直到 B[i] 与 B[j] 相等 或 匹配位置又回到了 待匹配字符串的开头(下面 j = j - 1,是因为 比较的第 j 个位置的不行,那么 相当于前置循环,又从for 开头的位置开始了,这等功效)
                while (B[i] != B[j] && j >= 1)
                    j = next[j - 1];
    
                // 前一个 while循环结果的判断
                if (B[i] == B[j])
                    next[i] = j + 1;
                else
                    next[i] = 0;
            }
            return next;
        }
    
        public static void match(String strA, String strB, int [] next)
        {
            char A[] = strA.toCharArray();
            char B[] = strB.toCharArray();
            int i = 0;
            int j = 0;
            while (i < A.length)
            {
                // 如果 A[i] = B[j]
                if (A[i] == B[j])
                {
                    i++;
                    j++;
                    // 如果 B字符串匹配完了一次
                    if (j == B.length)
                    {
                        // 输出完整匹配开头坐标在 A中的
                        System.out.println(i - B.length);
                        // 将 j 表示的坐标 回到 前一部分还可以用的位置 还比匹配  abcab  匹配的 abcab  那么 结束了之后 j 要回到 c 处,然后再与 i 比,而在 b 处的 next 值显而易见是等于 2 的,那么
                        j = next[j - 1];
                    }
                }
                else {
                    // 又回到了 待匹配字符串的开头了,回到原点, 下面进行 i++ 的原因是,如果 j==0 ,是会先进行上面的判断的,如果上面过不了,那么代表此时开头都不得匹配,只能再前进了
                    if (j == 0)
                        i++;
                    else
                        j = next[j - 1];   // 原因和上面 求 next 数组一样,既然不行,回到前缀处.
                }
            }
        }
    
        public static void main(String[] args) {
            String str = "abaabbabaab";
            System.out.println(Arrays.toString(Next(str)));
            match("abaabaabbabaaabaabbabaabaabbabaab", str, Next(str));
        }
    }
    
    
  • 相关阅读:
    go test 下篇
    go test 上篇
    利用Docker Compose快速搭建本地测试环境
    phinx:php数据库迁移
    tp5 r3 一个简单的SQL语句调试实例
    TP开发小技巧
    优酷真实视频地址解析——2014年10月7日
    霍夫变换
    Google Earth影像数据破解之旅
    线程理论:(四)锁优化
  • 原文地址:https://www.cnblogs.com/xmdykf/p/12509954.html
Copyright © 2011-2022 走看看