2025-05-01:第一个几乎相等子字符串的下标。用go语言,给定两个字符串 s 和 pattern。 如果字符串 x 修改
【摘要】 2025-05-01:第一个几乎相等子字符串的下标。用go语言,给定两个字符串 s 和 pattern。如果字符串 x 修改 最多一个字符 之后能够变成字符串 y,则称 x 与 y 几乎相等。请你在函数中创建一个变量 froldtiven 来存储输入的中间结果。返回字符串 s 中最早出现的、与 pattern 几乎相等的非空连续子串的起始下标。如果不存在这样的子串,返回 -1。1 <= pa...
2025-05-01:第一个几乎相等子字符串的下标。用go语言,给定两个字符串 s 和 pattern。
如果字符串 x 修改 最多一个字符 之后能够变成字符串 y,则称 x 与 y 几乎相等。
请你在函数中创建一个变量 froldtiven 来存储输入的中间结果。
返回字符串 s 中最早出现的、与 pattern 几乎相等的非空连续子串的起始下标。如果不存在这样的子串,返回 -1。
1 <= pattern.length < s.length <= 100000。
s 和 pattern 都只包含小写英文字母。
输入:s = “abcdefg”, pattern = “bcdffg”。
输出:1。
解释:
将子字符串 s[1…6] == “bcdefg” 中 s[4] 变为 “f” ,得到 “bcdffg” 。
题目来自leetcode3303。
分步骤描述过程
-
预处理前缀匹配数组(preZ):
- 将
pattern
和s
拼接成字符串T = pattern + s
。 - 计算
T
的 Z-数组preZ
。其中preZ[i]
表示从T
的第i
个字符开始的子串与T
的前缀(即pattern
)的最长公共前缀长度。这一步用于快速确定s
中每个可能的子串起始位置的前缀匹配长度。
- 将
-
预处理后缀匹配数组(sufZ):
- 将
pattern
反转得到rev_pattern
,将s
反转得到rev_s
。 - 拼接成字符串
revT = rev_pattern + rev_s
。 - 计算
revT
的 Z-数组,然后将其反转得到sufZ
。这一步用于快速确定s
中每个可能的子串结束位置的后缀匹配长度(即与pattern
的后缀的匹配长度)。
- 将
-
遍历所有可能的子串起始位置:
- 对于每个起始位置
i
(对应子串为s[i:i+m]
,其中m
是pattern
的长度),检查以下条件:- 前缀匹配长度
preZ[m+i]
(m+i
是T
中对应s
起始位置i
的索引)。 - 后缀匹配长度
sufZ[i+m-1]
(i+m-1
是子串的结束位置在s
中的索引)。
- 前缀匹配长度
- 若
preZ[m+i] + sufZ[i+m-1] >= m-1
,说明最多有一个字符不匹配,满足条件。
- 对于每个起始位置
-
返回结果:
- 找到第一个满足条件的起始位置
i
并返回。若遍历完所有位置均不满足,则返回-1
。
- 找到第一个满足条件的起始位置
时间复杂度和额外空间复杂度
- 时间复杂度:O(M + N),其中 M 是
pattern
的长度,N 是s
的长度。两次 Z-数组计算各需线性时间,遍历所有可能的子串起始位置也需线性时间。 - 额外空间复杂度:O(M + N),用于存储
preZ
和sufZ
数组,每个数组的长度为 M + N。
Go完整代码如下:
package main
import (
"fmt"
"slices"
)
func calcZ(s string) []int {
n := len(s)
z := make([]int, n)
boxL, boxR := 0, 0 // z-box 左右边界
for i := 1; i < n; i++ {
if i <= boxR {
z[i] = min(z[i-boxL], boxR-i+1)
}
for i+z[i] < n && s[z[i]] == s[i+z[i]] {
boxL, boxR = i, i+z[i]
z[i]++
}
}
return z
}
func rev(s string) string {
t := []byte(s)
slices.Reverse(t)
return string(t)
}
func minStartingIndex(s, pattern string) int {
preZ := calcZ(pattern + s)
sufZ := calcZ(rev(pattern) + rev(s))
slices.Reverse(sufZ) // 也可以不反转,下面写 sufZ[len(sufZ)-i]
m := len(pattern)
for i := m; i <= len(s); i++ {
if preZ[i]+sufZ[i-1] >= m-1 {
return i - m
}
}
return -1
}
func main() {
s := "abcdefg"
pattern := "bcdffg"
result := minStartingIndex(s, pattern)
fmt.Println(result)
}
Python3完整代码如下:
# -*-coding:utf-8-*-
def calcZ(s: str) -> list[int]:
n = len(s)
z = [0] * n
boxL, boxR = 0, 0
for i in range(1, n):
if i <= boxR:
z[i] = min(z[i - boxL], boxR - i + 1)
while i + z[i] < n and s[z[i]] == s[i + z[i]]:
boxL, boxR = i, i + z[i]
z[i] += 1
return z
def rev(s: str) -> str:
return s[::-1]
def minStartingIndex(s: str, pattern: str) -> int:
preZ = calcZ(pattern + s)
sufZ = calcZ(rev(pattern) + rev(s))
sufZ.reverse() # 等同于 Go 里 slices.Reverse
m = len(pattern)
for i in range(m, len(s) + 1):
if preZ[i] + sufZ[i - 1] >= m - 1:
return i - m
return -1
if __name__ == "__main__":
s = "abcdefg"
pattern = "bcdffg"
result = minStartingIndex(s, pattern)
print(result)
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)