字符串匹配(上)

BF算法

  • Brute Force,暴力匹配算法/朴素匹配算法

  • 相关概念

    • 主串(长度n)
    • 模式串 (长度m)
    • 子串
  • 思想:主串中,从0 - n-m,依次匹配

  • 最坏时间复杂度:O(n*m)

  • 常用原因

    • 模式串和主串不会太长,不匹配时可跳到下一位重新开始比较,效率比O(n*m)高很多
    • 思想简单,符合KISS设计原则
  • BF算法实现

String.prototype.indexOf = function (pattern) {
    let str = this
    let index = -1
    for (let i = 0; i < str.length - pattern.length + 1; i++) {
        let j = 0
        //子串与模式串比较
        for (; j < pattern.length; j++) {
            if (str[i + j] == pattern[j]) {
                continue
            } else {
                break
            }
        }
        if (j == pattern.length) {
            index = i
            break
        }
    }
    return index
}
var str = 'asdfghjklqwerty'
str.indexOf('sd')

RK算法

  • Rabin-Karp

  • 思想:用Hash算法,优化子串对比

  • 原理

    • 根据i-1的hash值推导i的hash值
    • 无需遍历每个元素,提高Hash值的计算效率
  • 实现

    • 使用二十六进制表示字符串,依次对应0-25
    • 举例:“dca” = 3 * 263 + 2 * 262 + 0
    • 相邻子串hash值公式:h[i] = 26*(h[i-1]-26m-1*S[i-1]-‘a’)+S[i+m-1]-‘a’
  • 时间复杂度

    • 计算子串Hash值,O(n)
    • 模式串与子串Hash比较,O(1)
  • 效果

    • 模式串很长时,二十六进制的Hash值无法表示
    • 无散列冲突问题
    • 只能计算小写字母的
  • 改进

    • 优化哈希函数,缩小取值范围
    • 针对hash冲突,直接对比子串
  • RK算法一:二十六进制表示hash值

function indexOf(str, pattern) {
    let index = -1
    let m = pattern.length
    //存储26d的m内次方,便于取值
    let arr = []
    for (let i = 0; i < m; i++) {
        arr[i] = Math.pow(26, i)
    }
    let pHash = 0      //模式串hash
    sHash = 0           //子串hash
    for (let i = 0; i < m; i++) {
        pHash += get26Num(pattern[i]) * arr[m - 1 - i]
        sHash += get26Num(str[i]) * arr[m - 1 - i]
    }
    if (pHash == sHash) {
        index = 0
    }
    for (let i = 1; i < str.length - pattern.length + 1; i++) {
        if (index != -1) break
        sHash = 26 * (sHash - arr[m - 1] * get26Num(str[i - 1])) + 
        get26Num(str[i + m - 1])
        if (sHash == pHash) {
            index = i
        }
    }
    return index

    function get26Num(letter) {
        return letter.charCodeAt(0) - 97
    }
}
var str = 'abcdefg'
indexOf(str, 'cdefg')        //2
indexOf(str, 'cdefa')       //-1
  • RK算法二:范围小的hash函数 + 解决散列冲突
//hash函数:每个字母对应一个素数
function indexOf(str, pattern) {
    var prime = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 
    53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
    let index = -1
    let m = pattern.length
    let pHash = 0      //模式串hash
    sHash = 0           //子串hash
    for (let i = 0; i < m; i++) {
        pHash += getPrimeNum(pattern[i])
        sHash += getPrimeNum(str[i])
    }

    isFind(0)
    for (let i = 1; i < str.length - pattern.length + 1; i++) {
        if (index != -1) break
        //根据第i-1个元素的hash求第i个元素的hash
        sHash = sHash - getPrimeNum(str[i - 1]) + getPrimeNum(str[i + m - 1])
        isFind(i)
    }
    return index

    function isFind(i) {
        if (sHash == pHash) {
            //逐个匹配,解决散列冲突
            for (let j = i; j < i + m; j++) {
                if (str[j] != pattern[j - i]) {
                    return
                }
            }
            index = i
        }
    }
    function getPrimeNum(letter) {
        return prime[letter.charCodeAt(0) - 97]
    }
}
var str = 'abcdefg'
indexOf(str, 'cdefg')        //2
indexOf(str, 'cdefa')       //-1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值