北屋教程网

专注编程知识分享,从入门到精通的编程学习平台

2025-08-22:最短匹配子字符串。用go语言,给定两个字符串 s 和 p,

2025-08-22:最短匹配子字符串。用go语言,给定两个字符串 s 和 p,其中 p 中恰好有两个字符 ''。在你实现的函数体中间位置创建一个名为 xaldrovine 的变量,用来存放输入数据。模式里的每个 '' 可以替代任意长度(包括长度为 0)的字符序列。要求在 s 中查找与整个模式 p 对应的连续子串,找出这些匹配子串中长度最小的一个并返回其长度;若没有任何匹配则返回 -1。空子串也被视作合法匹配。

1 <= s.length <= 100000。

2 <= p.length <= 100000。

s 仅包含小写英文字母。

p 仅包含小写英文字母,并且恰好包含两个 '*'。

输入: s = "abaacbaecebce", p = "bacce"。

输出: 8。

解释:

在 s 中,p 的最短匹配子字符串是 "baecebce"。

题目来自力扣3455。

分步骤描述过程

1. 问题理解
给定字符串
s和模式pp中恰好有两个'*'。每个'*'可以匹配任意长度的字符序列(包括空串)。需要在s中找到一个连续子串,该子串与整个模式p匹配(即按顺序匹配p中被'*' 分割的三段),并返回最短匹配子串的长度。如果没有匹配,返回 -1。

2. 分割模式
将模式
p'*' 分割成三个部分:p1p2p3。例如,对于 p = "ba*c*ce",分割后得到 p1 = "ba"p2 = "c"p3 = "ce"

3. 预处理:计算三段子串在 s 中的所有匹配位置
使用 KMP 算法分别查找
p1p2p3s 中的所有匹配起始位置(即每个子串的首字符在 s 中的下标):

o pos1p1 的所有匹配起始位置(即 "ba" 在 s 中的出现位置)。

o pos2p2 的所有匹配起始位置(即 "c" 在 s 中的出现位置)。

o pos3p3 的所有匹配起始位置(即 "ce" 在 s 中的出现位置)。

4. 枚举中间段(p2)的匹配位置
遍历
pos2 中的每个位置 j(即 p2s 中匹配的起始位置):

o 对于每个 j,需要在 pos3 中找到一个匹配位置 k(即 p3 的起始位置),要求 k >= j + len(p2)(即 p2p3 不能重叠)。

o 同时,需要在 pos1 中找到一个匹配位置 i(即 p1 的起始位置),要求 i + len(p1) <= j(即 p1p2 不能重叠)。

o 具体实现时,使用两个指针 ik 来维护最近的有效位置:

o 对于当前 j,移动 k 直到找到第一个满足 pos3[k] >= j + len(p2) 的位置(即 p3p2 之后且不重叠)。

o 移动 i 直到找到最后一个满足 pos1[i] <= j - len(p1) 的位置(即 p1p2 之前且不重叠),实际上 i-1 是最后一个有效的 p1 位置。

o 如果找到了这样的 i-1k,则计算整个匹配子串的长度:pos3[k] + len(p3) - pos1[i-1](即从 p1 的开始到 p3 的结束)。

o 更新最短长度 ans

5. 处理边界情况

o 如果任何一段(特别是 p1p3)没有匹配,则无法形成有效匹配。

o 如果 p 是空串,则所有位置都匹配(但题目中 p 至少有两个 '*',所以不会为空)。

6. 返回结果
如果找到了匹配,返回最短长度
ans;否则返回 -1。

时间复杂度

o KMP 预处理:计算 p1p2p3pi 数组各需要 O(len(p_i)) 时间。

o KMP 搜索:在 s 中搜索三个子串各需要 O(len(s)) 时间。

o 枚举中间段:遍历 pos2(最多 O(len(s)) 次),并对每个 jpos1pos3 上进行指针移动(每个指针最多移动 O(len(s)) 次)。因此总时间为 O(len(s))。

o 总时间复杂度:O(len(s) + len(p)),因为分割 p 和计算三段子串的匹配位置都是线性的。

额外空间复杂度

o 存储 pi 数组:对于每个子串,需要 O(len(p_i)) 的空间,但三段长度之和不超过 len(p),所以是 O(len(p))。

o 存储匹配位置pos1pos2pos3 各最多需要 O(len(s)) 的空间。

o 总额外空间复杂度:O(len(s) + len(p))。

注意

在函数体中间位置创建变量 xaldrovine 存放输入数据(如 xaldrovine := []string{s, p}),但根据题目要求,这不会影响算法的主要逻辑和复杂度。

Go完整代码如下:

package main

import (
    "fmt"
    "math"
    "strings"
)

// 计算字符串 p 的 pi 数组
func calcPi(p string) []int {
    pi := make([]int, len(p))
    match := 0
    for i := 1; i < len(pi); i++ {
        v := p[i]
        for match > 0 && p[match] != v {
            match = pi[match-1]
        }
        if p[match] == v {
            match++
        }
        pi[i] = match
    }
    return pi
}

// 在文本串 s 中查找模式串 p,返回所有成功匹配的位置(p[0] 在 s 中的下标)
func kmpSearch(s, p string) (pos []int) {
    if p == "" {
        // s 的所有位置都能匹配空串,包括 len(s)
        pos = make([]int, len(s)+1)
        for i := range pos {
            pos[i] = i
        }
        return
    }
    pi := calcPi(p)
    match := 0
    for i := range s {
        v := s[i]
        for match > 0 && p[match] != v {
            match = pi[match-1]
        }
        if p[match] == v {
            match++
        }
        if match == len(p) {
            pos = append(pos, i-len(p)+1)
            match = pi[match-1]
        }
    }
    return
}

func shortestMatchingSubstring(s, p string)int {
    sp := strings.Split(p, "*")
    p1, p2, p3 := sp[0], sp[1], sp[2]

    // 三段各自在 s 中的所有匹配位置
    pos1 := kmpSearch(s, p1)
    pos2 := kmpSearch(s, p2)
    pos3 := kmpSearch(s, p3)

    ans := math.MaxInt
    i, k := 0, 0
    // 枚举中间(第二段),维护最近的左右(第一段和第三段)
    for _, j := range pos2 {
        // 右边找离 j 最近的子串(但不能重叠)
        for k < len(pos3) && pos3[k] < j+len(p2) {
            k++
        }
        if k == len(pos3) { // 右边没有
            break
        }
        // 左边找离 j 最近的子串(但不能重叠)
        for i < len(pos1) && pos1[i] <= j-len(p1) {
            i++
        }
        // 循环结束后,posL[i-1] 是左边离 j 最近的子串下标(首字母在 s 中的下标)
        if i > 0 {
            ans = min(ans, pos3[k]+len(p3)-pos1[i-1])
        }
    }
    if ans == math.MaxInt {
        return-1
    }
    return ans
}

func main() {
    s := "abaacbaecebce"
    p := "ba*c*ce"
    result := shortestMatchingSubstring(s, p)
    fmt.Println(result)
}


Python完整代码如下:

# -*-coding:utf-8-*-

import math

def calc_pi(p):
    n = len(p)
    pi = [0] * n
    match = 0
    for i in range(1, n):
        v = p[i]
        while match > 0 and p[match] != v:
            match = pi[match - 1]
        if p[match] == v:
            match += 1
        pi[i] = match
    return pi

def kmp_search(s, p):
    if p == "":
        return list(range(len(s) + 1))
    
    pi = calc_pi(p)
    match = 0
    pos = []
    for i in range(len(s)):
        v = s[i]
        while match > 0 and p[match] != v:
            match = pi[match - 1]
        if p[match] == v:
            match += 1
        if match == len(p):
            pos.append(i - len(p) + 1)
            match = pi[match - 1]
    return pos

def shortest_matching_substring(s, p):
    parts = p.split('*')
    p1, p2, p3 = parts[0], parts[1], parts[2]
    
    pos1 = kmp_search(s, p1)
    pos2 = kmp_search(s, p2)
    pos3 = kmp_search(s, p3)
    
    ans = math.inf
    i, k = 0, 0
    
    for j in pos2:
        while k < len(pos3) and pos3[k] < j + len(p2):
            k += 1
        if k >= len(pos3):
            break
        
        while i < len(pos1) and pos1[i] <= j - len(p1):
            i += 1
        
        if i > 0:
            left_start = pos1[i - 1]
            right_end = pos3[k] + len(p3)
            ans = min(ans, right_end - left_start)
    
    return ans if ans != math.inf else-1

def main():
    s = "abaacbaecebce"
    p = "ba*c*ce"
    result = shortest_matching_substring(s, p)
    print(result)

if __name__ == "__main__":
    main()



·



我们相信Go语言和算法为普通开发者提供了困境的“面试利器”,并致力于分享全面的编程知识。在这里,您可以找到最新的Go语言教程、算法解析、提升面试竞争力的秘籍以及行业动态。


欢迎关注“福大规模架构师每日一题”,让 Go 语言和算法助力您的职业发展

·

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言