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

     /*
            比如 关键字 key ('abcf') 匹配字符串SS ('abcd abcf')
    
             当比较到d的时候, 发现不对, 那为了节省效率, 可以直跳过abc,
             跳跃到d开始再比较, 这里是没问题的
    
            但是当 关键字 key('abcabd') 匹配字符串 SS( 'abcabcabd') 
            比较到第二个c的时候, 发现d != c ,显然是不能直接跳跃到c开始比较的
    
            应该从第二个ab开始, 要找出这个ab可利用 前缀 = 后缀的方法
    
            跳3位, 就到了ab, 这3位是怎么算出来的, 已经匹配的abcab 5位 - ab的 2位得到
    
            比如 key(abcabd) 切前面5位就是 abcab 
    
            前缀是 a   ab   abc   abca
            后缀   b   ab   cab   bcab
    
    
            可以看到 ab是在关键字切割成一部分后, 存在前缀和后缀相等的情况
    
            这种情况在匹配字符串SS 的时候, 第一次遍历SS的时候,如果abca都匹配成功 ,a就匹配了两次,  如果或者abcab都匹配成功, ab就匹配了两次
    
            所以这个关键字可以标记成 [ a, b, c, 1, 2, d ]


       反过来

        1. 如果一个关键字没有 前缀和后缀相等的情况, 则可以直接跳过已经匹配的, 如

         SS: abcdffff

        key: abcde

        2. 如果有前缀 或者情况, 但匹配到 e的时候才发现不匹配, 而且前一个f在 map表为0, 也是可以直接跳过的

    因为既然最后的 abf, 在前缀不存在abf, 所以不需要跳到第二个ab开始去匹配, 直接跳到第二个f就可以了

       SS: abcdabf fgggg
        
        key: abcdabf e
        
       3. 再看一个复杂一点的情况,
         
        SS: abc abc accab

       key: abc abc ab
         map: 000 123 12
      
    根据前面 已匹配 - 上一个map值; 会跳到第3个a 去比较, 为什么不跳到第二个abc开始呢,

    */ function getMap(str) { var prefix = []; var suffix = []; var map = []; for (var i = 0, j = str.length; i < j; i++) { var newStr = str.substring(0, i + 1); if (newStr.length == 1) { map[i] = 0; } else {       //每跳一个字符, 就查看这截字符串 有没有前缀后缀匹配的情况 for (var k = 0; k < i; k++) { //取前缀 prefix[k] = newStr.slice(0, k + 1); //取后缀 suffix[k] = newStr.slice(-k - 1); //如果前后缀相等 if (prefix[k] == suffix[k]) { // 1 2 map[i] = prefix[k].length; } } if (!map[i]) { map[i] = 0; } } } return map; } function KMP(SS, key) { //生成匹配表 [0,0,0,1,2,0] var part = getMap(key); var sslen = SS.length; var keylen = key.length; var result; var i = 0; var j = 0; for (i=0; i < SS.length; i++) { //最外层循环,主串 //子循环 for ( j = 0; j < keylen; j++) { //如果ss字符 和key 字符相等 if (key.charAt(j) == SS.charAt(i)) { //如果最后一个字符也相等, 说明匹配完成, 有结果, 跳出内循环 if (j == keylen - 1) { result = i - j; break; } else { i++; } } else {         //abcabf
            //00012 part[j] 是 f 的位置
            //abcabd
            if (j > 1 && part[j - 1] > 0) { // 已匹配的 减去 部分相等前缀长度, 最好调试一下这里
               //回溯
    i -= part[j-1] console.log(i) } else { //跳过已经匹配的 或者 没有匹配的 //i = i - j;
              

              //如果有匹配的字符, 因为排除了kmp情况, 直接跳过已经匹配过的
                     if(j>0){
                        //i 其实保持不变, 因为跳出内循环后, 外循环i会+1
                        i -= 1;
                        console.log(i + 'bb')
                     }else{
                       //移动一位
                       i = i - j;
                     }

    
    
                  }
             //因为不匹配, 所以跳出本次关键字对比
                   break;
                }
    
    
            }
    
            //如果有结果退出当前循环
            if (result || result == 0) {
              break;
            }
        }
    
    
            //如果没有结果
            if (result || result == 0) {
              return result
            } else {
              return -1;
            }
    } 
      var s = "abcabfababcabffffffabcabd";
      var k = "abcabd";
     
     
       var r =  KMP(s,k)
    
       console.log(r)
    
    
     
  • 相关阅读:
    ASP.NET Ajax基础-1
    项目管理必读之书-》人月神话
    Discuz2.5菜鸟解析-1
    Jquery初学者指南-1
    敏捷日记
    精品图书大推荐2
    Jquery初学者指南-2
    纯javaScript脚本来实现Ajax功能例子一
    周五面试笑话一则
    JavaScript基础-4
  • 原文地址:https://www.cnblogs.com/dhsz/p/6707290.html
Copyright © 2011-2022 走看看