2025-09-06:字典序最小的生成字符串。用go语言,给定两个字符串 str1(长度为 n)和 str2(长度为 m)。我们要构造一个长度为 n + m − 1 的字符串 word,并且对每个下标

『AI先锋杯·14天征文挑战第5期』 10w+人浏览 780人参与

2025-09-06:字典序最小的生成字符串。用go语言,给定两个字符串 str1(长度为 n)和 str2(长度为 m)。我们要构造一个长度为 n + m − 1 的字符串 word,并且对每个下标 i(0 ≤ i ≤ n−1)都要满足一个约束:

  • 当 str1[i] 为字符 ‘T’ 时,word 从位置 i 开始、长度为 m 的连续片段必须与 str2 完全相同;

  • 当 str1[i] 为字符 ‘F’ 时,word 从位置 i 开始、长度为 m 的连续片段必须与 str2 不相同。

在满足上述所有约束的前提下,返回按字典序最小的那个 word;若没有任何字符串能满足这些条件,则返回空串 “”。字典序比较按常规字母表顺序进行:先比较第一个不同字符,字符更靠前的字符串更小;若一个字符串是另一个的前缀,则较短者更小。子串指字符串中连续的一段字符。

1 <= n == str1.length <= 10000。

1 <= m == str2.length <= 500。

str1 仅由 ‘T’ 或 ‘F’ 组成。

str2 仅由小写英文字母组成。

输入: str1 = “TFTF”, str2 = “ab”。

输出: “ababa”。

解释:

下表展示了字符串 “ababa” 的生成过程:

下标T/F长度为 m 的子字符串
0‘T’“ab”
1‘F’“ba”
2‘T’“ab”
3‘F’“ba”

字符串 “ababa” 和 “ababb” 都可以由 str1 和 str2 生成。

返回 “ababa”,因为它的字典序更小。

题目来自力扣3474。

分步骤描述过程

1. 初始化结果字符串

  • 创建一个长度为 n + m - 1 的字节数组 ans,初始时每个字符都是 '?'(表示待定位置)。

2. 处理 str1 中的 'T' 约束

  • 遍历 str1,对于每个 'T' 的位置 i
    • 找到前一个 'T' 出现的位置 pre(初始为 -m),计算重叠部分的大小 size = max(pre + m - i, 0)。这表示当前 'T' 要求的子串与前一个 'T' 要求的子串有 size 个字符的重叠。
    • 检查 str2 的长为 size 的前缀和后缀是否相同(使用 Z 函数计算 str2z 数组,检查 z[m - size] 是否等于 size)。如果不相同,则无法满足约束,返回空串。
    • str2 从索引 size 开始的部分复制到 ans 中从 i + size 开始的位置(即填充非重叠部分)。
  • 这样,所有 'T' 约束要求的子串都被正确填充(重叠部分已验证,非重叠部分直接复制)。

3. 预处理待定位置('?'

  • 创建一个数组 preQ,记录每个位置之前(包括自身)最近的待定位置(即 ans 中值为 '?' 的位置)的索引。
  • 同时,将所有待定位置初始化为 'a'(字典序最小)。

4. 处理 str1 中的 'F' 约束

  • 再次使用 Z 函数,计算 str2 + ans 的 Z 数组(用于快速判断任意位置开始的子串是否等于 str2)。
  • 遍历 str1,对于每个 'F' 的位置 i
    • 检查从 i 开始的子串是否等于 str2(通过 Z 数组:z[m + i] >= m 表示相等)。如果相等,则需要修改待定位置来破坏匹配。
    • 找到子串 ans[i : i+m] 中最后一个待定位置 j(通过 preQ[i+m-1] 获取)。如果没有待定位置(j < i),则无法破坏匹配,返回空串。
    • ans[j] 改为 'b'(因为 'a' 已经不能避免匹配,所以改为稍大的字符,但字典序仍尽量小),并跳过后续检查(直接让 i = j,避免重复修改同一区域)。
  • 这样,每个 'F' 约束要求的子串都不等于 str2(通过修改最后一个待定位置为 'b' 来破坏匹配)。

5. 返回结果

  • 如果所有约束都满足,返回 ans 转换后的字符串;否则返回空串。

总的时间复杂度和额外空间复杂度

  • 时间复杂度

    • 计算 Z 函数:每次计算的时间复杂度为 O(|字符串长度|)。这里计算了两次 Z 函数:一次用于 str2(长度为 m),一次用于 str2 + ans(长度为 m + (n+m-1) = n+2m-1)。由于 m <= 500n <= 10000,所以总时间复杂度为 O(n + m)。
    • 处理 'T' 约束:遍历 str1'T' 位置,每次操作是常数时间(除了复制操作,但复制总长度不超过 n+m),所以为 O(n)。
    • 预处理待定位置:遍历 ans(长度为 n+m-1),O(n+m)。
    • 处理 'F' 约束:遍历 str1'F' 位置,每次检查 Z 数组是常数时间,修改待定位置也是常数时间,所以为 O(n)。
    • 总体时间复杂度为 O(n + m)
  • 额外空间复杂度

    • Z 数组:最大为 O(n + m)(第二次计算 Z 函数时)。
    • 结果字符串 ans:长度为 n+m-1
    • 预处理数组 preQ:长度为 n+m-1
    • 总体额外空间复杂度为 O(n + m)

Go完整代码如下:

package main

import (
	"bytes"
	"fmt"
)

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]++
		}
	}
	z[0] = n
	return z
}

func generateString(s, t string) string {
	n, m := len(s), len(t)
	ans := bytes.Repeat([]byte{'?'}, n+m-1)

	// 处理 T
	pre := -m
	z := calcZ(t)
	for i, b := range s {
		if b != 'T' {
			continue
		}
		size := max(pre+m-i, 0)
		// t 的长为 size 的前后缀必须相同
		if size > 0 && z[m-size] < size {
			return ""
		}
		// size 后的内容都是 '?',填入 t
		copy(ans[i+size:], t[size:])
		pre = i
	}

	// 计算 <= i 的最近待定位置
	preQ := make([]int, len(ans))
	pre = -1
	for i, c := range ans {
		if c == '?' {
			ans[i] = 'a' // 待定位置的初始值为 a
			pre = i
		}
		preQ[i] = pre
	}

	// 找 ans 中的等于 t 的位置,可以用 KMP 或者 Z 函数
	z = calcZ(t + string(ans))

	// 处理 F
	for i := 0; i < n; i++ {
		if s[i] != 'F' {
			continue
		}
		// 子串必须不等于 t
		if z[m+i] < m {
			continue
		}
		// 找最后一个待定位置
		j := preQ[i+m-1]
		if j < i { // 没有
			return ""
		}
		ans[j] = 'b'
		i = j // 直接跳到 j
	}

	return string(ans)
}

func main() {
	str1 := "TFTF"
	str2 := "ab"
	result := generateString(str1, str2)
	fmt.Println(result)
}

在这里插入图片描述

Python完整代码如下:

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

def calc_z(s):
    n = len(s)
    z = [0] * n
    box_l, box_r = 0, 0
    for i in range(1, n):
        if i <= box_r:
            z[i] = min(z[i - box_l], box_r - i + 1)
        while i + z[i] < n and s[z[i]] == s[i + z[i]]:
            box_l, box_r = i, i + z[i]
            z[i] += 1
    z[0] = n
    return z

def generate_string(s, t):
    n, m = len(s), len(t)
    ans = ['?'] * (n + m - 1)
    
    # 处理 T
    pre = -m
    z_t = calc_z(t)
    for i, char in enumerate(s):
        if char != 'T':
            continue
        size = max(pre + m - i, 0)
        if size > 0 and z_t[m - size] < size:
            return ""
        # 将 t[size:] 复制到 ans[i+size:]
        for j in range(size, m):
            if i + j < len(ans):
                ans[i + j] = t[j]
        pre = i
    
    # 预处理最近待定位置
    pre_q = [-1] * len(ans)
    pre_val = -1
    for i in range(len(ans)):
        if ans[i] == '?':
            ans[i] = 'a'  # 初始化为 'a'
            pre_val = i
        pre_q[i] = pre_val
    
    # 计算整个字符串的 Z 数组
    concat_str = t + ''.join(ans)
    z_total = calc_z(concat_str)
    
    # 处理 F
    i = 0
    while i < n:
        if s[i] != 'F':
            i += 1
            continue
        # 检查从位置 i 开始的子串是否等于 t
        if z_total[m + i] >= m:
            # 需要修改最后一个待定位置
            j = pre_q[i + m - 1]
            if j < i:
                return ""
            ans[j] = 'b'
            i = j  # 跳到修改的位置
        i += 1
    
    return ''.join(ans)

def main():
    str1 = "TFTF"
    str2 = "ab"
    result = generate_string(str1, str2)
    print(result)

if __name__ == "__main__":
    main()

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

福大大架构师每日一题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值