GO对于常见的字符串匹配算法的实现 |青训营

157 阅读2分钟

在Go语言中进行字符串匹配算法常见的包括包括暴力匹配、KMP算法和Boyer-Moore算法。

暴力匹配算法

暴力匹配算法是一种简单直观的字符串匹配方法。它从主串中的每一个可能的位置开始,尝试与模式串进行匹配,如果匹配失败,则将主串的位置后移一位,直到找到匹配或主串被遍历完。

原理:

  1. 从主串的第一个字符开始,与模式串的第一个字符进行比较。
  2. 如果匹配成功,继续比较主串和模式串的下一个字符,直到匹配完成或出现不匹配的字符。
  3. 如果不匹配,将主串的位置后移一位,再次与模式串的第一个字符进行比较,重复此过程。

下面是Go中的暴力匹配算法示例:

func bruteForceMatch(mainStr, patternStr string) int {
    mainLen := len(mainStr)
    patternLen := len(patternStr)

    for i := 0; i <= mainLen-patternLen; i++ {
        j := 0
        for j < patternLen && mainStr[i+j] == patternStr[j] {
            j++
        }
        if j == patternLen {
            return i // 匹配成功,返回主串中的位置
        }
    }
    return -1 // 未找到匹配
}

KMP算法

KMP算法是一种高效的字符串匹配算法,它利用了模式串的前缀信息,避免了不必要的比较。

原理:

  1. 构建部分匹配表(也称为前缀表),用于记录模式串中每个位置的最长公共前后缀长度。
  2. 在匹配过程中,根据部分匹配表的信息,调整主串和模式串的比较位置,以减少不必要的比较。
  3. 当发生不匹配时,通过部分匹配表,将模式串向右移动尽可能远的位置,继续匹配。

下面是Go中的KMP算法示例:

func buildPartialMatchTable(patternStr string) []int {
    patternLen := len(patternStr)
    table := make([]int, patternLen)
    table[0] = 0

    j := 0
    for i := 1; i < patternLen; i++ {
        for j > 0 && patternStr[i] != patternStr[j] {
            j = table[j-1]
        }
        if patternStr[i] == patternStr[j] {
            j++
        }
        table[i] = j
    }
    return table
}

func KMPSearch(mainStr, patternStr string) int {
    mainLen := len(mainStr)
    patternLen := len(patternStr)
    table := buildPartialMatchTable(patternStr)

    j := 0
    for i := 0; i < mainLen; i++ {
        for j > 0 && mainStr[i] != patternStr[j] {
            j = table[j-1]
        }
        if mainStr[i] == patternStr[j] {
            j++
        }
        if j == patternLen {
            return i - patternLen + 1 // 匹配成功,返回主串中的位置
        }
    }
    return -1 // 未找到匹配
}

Boyer-Moore算法

Boyer-Moore算法是一种高效的字符串匹配算法,它通过从右向左的方式进行匹配,利用坏字符规则和好后缀规则来跳过不必要的比较。

原理:

  1. 预处理模式串,生成坏字符表和好后缀表。
  2. 在匹配过程中,从右向左比较模式串和主串,根据坏字符表和好后缀表,调整模式串的位置。
  3. 根据坏字符表找到不匹配字符在模式串中的最右位置,根据好后缀表找到好后缀的匹配段。

以下是Go中的Boyer-Moore算法示例:

func buildBadCharTable(patternStr string) map[byte]int {
    table := make(map[byte]int)
    patternLen := len(patternStr)
    for i := 0; i < patternLen-1; i++ {
        table[patternStr[i]] = patternLen - 1 - i
    }
    return table
}

func buildGoodSuffixTable(patternStr string) []int {
    patternLen := len(patternStr)
    table := make([]int, patternLen)
    suffix := make([]int, patternLen)

    for i := patternLen - 2; i >= 0; i-- {
        j := i
        for j >= 0 && patternStr[j] == patternStr[patternLen-1-i+j] {
            j--
        }
        suffix[i] = i - j
    }

    for i := 0; i < patternLen-1; i++ {
        table[i] = patternLen
    }
    for i := 0; i < patternLen-1; i++ {
        table[patternLen-1-suffix[i]] = patternLen - 1 - i
    }

    return table
}

func BoyerMooreSearch(mainStr, patternStr string) int {
    mainLen := len(mainStr)
    patternLen := len(patternStr)
    badCharTable := buildBadCharTable(patternStr)
    goodSuffixTable := buildGoodSuffixTable(patternStr)

    i := patternLen - 1
    for i < mainLen {
        j := patternLen - 1
        for j >= 0 && mainStr[i] == patternStr[j] {
            i--
            j--
        }
        if j < 0 {
            return i + 1 // 匹配成功,返回主串中的位置
        }
        moveByBadChar := j - badCharTable[mainStr[i]]
        moveByGoodSuffix := 0
        if j < patternLen-1 {
            moveByGoodSuffix = moveByGoodSuffixRule(j, patternLen, goodSuffixTable)
        }
        i += max(moveByBadChar, moveByGoodSuffix)
    }
    return

 -1 // 未找到匹配
}

func moveByGoodSuffixRule(badCharIdx, patternLen int, goodSuffixTable []int) int {
    k := patternLen - 1 - badCharIdx
    if goodSuffixTable[k] != -1 {
        return badCharIdx - goodSuffixTable[k] + 1
    } else {
        for r := badCharIdx + 2; r <= patternLen-1; r++ {
            if isPrefix(patternLen, patternLen-r, patternLen-1) {
                return r
            }
        }
        return patternLen
    }
}

func isPrefix(p, i, j int) bool {
    for i < p && j < p {
        if patternStr[i] != patternStr[j] {
            return false
        }
        i++
        j++
    }
    return true
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

在上述代码中,BoyerMooreSearch函数实现了Boyer-Moore算法,通过坏字符表和好后缀表来进行匹配。buildBadCharTablebuildGoodSuffixTable函数用于构建坏字符表和好后缀表。moveByGoodSuffixRule函数用于根据好后缀规则计算移动距离。