2025-05-26:字符串转换后的长度Ⅱ。用go语言,你有一个只包含小写字母的字符串 s,一个整数 t 表示转换次数,还有一个

举报
福大大架构师每日一题 发表于 2025/05/26 07:21:51 2025/05/26
【摘要】 2025-05-26:字符串转换后的长度Ⅱ。用go语言,你有一个只包含小写字母的字符串 s,一个整数 t 表示转换次数,还有一个长度为 26 的数组 nums。每次转换过程如下:对字符串 s 中的每个字符 s[i],用字母表中紧跟该字母后面连续的 nums[s[i]-‘a’] 个字符替换它。超过字母表 ‘z’ 的部分,会从字母表开头重新开始计数(即循环回绕)。例如,字符 ‘a’ 且对应 nu...

2025-05-26:字符串转换后的长度Ⅱ。用go语言,你有一个只包含小写字母的字符串 s,一个整数 t 表示转换次数,还有一个长度为 26 的数组 nums。每次转换过程如下:

  • 对字符串 s 中的每个字符 s[i],用字母表中紧跟该字母后面连续的 nums[s[i]-‘a’] 个字符替换它。

  • 超过字母表 ‘z’ 的部分,会从字母表开头重新开始计数(即循环回绕)。

例如,字符 ‘a’ 且对应 nums[0] = 3,则它被替换成 ‘b’、‘c’、‘d’ 三个字母。如果字符是 ‘y’ 且 nums[24] = 3,则替换成 ‘z’、‘a’、‘b’。

经过 t 次这样的转换后,返回最终字符串的长度(结果对 1000000007 取模)。

1 <= s.length <= 100000。

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

1 <= t <= 1000000000。

nums.length == 26。

1 <= nums[i] <= 25。

输入: s = “abcyy”, t = 2, nums = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2]。

输出: 7。

解释:
第一次转换 (t = 1)

‘a’ 变为 ‘b’ 因为 nums[0] == 1

‘b’ 变为 ‘c’ 因为 nums[1] == 1

‘c’ 变为 ‘d’ 因为 nums[2] == 1

‘y’ 变为 ‘z’ 因为 nums[24] == 1

‘y’ 变为 ‘z’ 因为 nums[24] == 1

第一次转换后的字符串为: “bcdzz”

第二次转换 (t = 2)

‘b’ 变为 ‘c’ 因为 nums[1] == 1

‘c’ 变为 ‘d’ 因为 nums[2] == 1

‘d’ 变为 ‘e’ 因为 nums[3] == 1

‘z’ 变为 ‘ab’ 因为 nums[25] == 2

‘z’ 变为 ‘ab’ 因为 nums[25] == 2

第二次转换后的字符串为: “cdeabab”

字符串最终长度: 字符串为 “cdeabab”,长度为 7 个字符。

题目来自力扣3337。

解题思路

直接模拟每次转换的过程是不可行的,因为 t 可以达到 1e9,而字符串长度可以达到 1e5,时间复杂度会爆炸(O(t * len(s)))。

矩阵快速幂优化:

观察到每次转换中,每个字符的替换是独立的,且替换后的字符数量取决于当前字符和 nums 数组。我们可以用矩阵乘法来表示字符的转换关系:

  • 定义一个 26x26 的转移矩阵 T,其中 T[i][j] 表示字符 j 在转换后对字符 i 的贡献(即字符 j 会被替换为多少个字符 i)。
    • 对于字符 j,它会替换为 nums[j] 个连续字符,分别是 (j+1)%26, (j+2)%26, …, (j+nums[j])%26
    • 因此,T[(j+k)%26][j] = 1,其中 k1nums[j]
  • 初始时,统计字符串 s 中每个字符的频率 ff[i] 表示字符 'a'+i 的出现次数)。
  • 经过 t 次转换后,字符的频率向量 f_t 可以通过矩阵快速幂计算:f_t = T^t * f
  • 最终字符串的长度是 f_t 中所有元素的和。

具体步骤:

  1. 构建转移矩阵 T
    • 对于每个字符 j025),遍历 k1nums[j],设置 T[(j+k)%26][j] = 1
    • 这样,T[i][j] 表示字符 j 在转换后会贡献多少个字符 i
  2. 初始频率 f
    • 统计 s 中每个字符的出现次数。
  3. 计算 T^t
    • 使用矩阵快速幂高效计算 Tt 次幂。
  4. 计算 f_t = T^t * f
    • 矩阵乘法计算新的频率向量。
  5. 求和:
    • f_t 中所有元素的和就是最终字符串的长度。

时间复杂度和空间复杂度

  • 时间复杂度
    • 构建转移矩阵 TO(26 * max(nums))O(26 * 25) = O(650)
    • 矩阵快速幂:每次矩阵乘法是 O(26^3) = O(17576),共 O(log t) 次,因此是 O(17576 * log t)
    • 计算 f_t:矩阵乘向量是 O(26^2) = O(676)
    • 总时间复杂度:O(650 + 17576 * log t + 676)O(log t)(因为 26^3 是常数)。
  • 空间复杂度
    • 转移矩阵 T 和中间矩阵:O(26^2) = O(676)
    • 频率向量:O(26)
    • 总空间复杂度:O(1)(常数空间,与输入规模无关)。

总结

通过矩阵快速幂,我们将问题从 O(t * len(s)) 优化到 O(log t) 的时间复杂度,能够高效处理 t 很大的情况。空间复杂度是常数 O(1)

Go完整代码如下:

package main

import (
	"fmt"
)

const MOD = 1e9 + 7
const L = 26

func lengthAfterTransformations(s string, t int, nums []int) int {
	T := NewMat()
	for i := 0; i < L; i++ {
		for j := 1; j <= nums[i]; j++ {
			T.a[(i+j)%L][i] = 1
		}
	}

	res := quickMul(T, t)
	f := make([]int, L)
	for _, ch := range s {
		f[ch-'a']++
	}

	ans := 0
	for i := 0; i < L; i++ {
		for j := 0; j < L; j++ {
			ans = (ans + res.a[i][j]*f[j]) % MOD
		}
	}
	return ans
}

type Mat struct {
	a [L][L]int
}

func NewMat() *Mat {
	return &Mat{}
}

func NewMatCopy(from *Mat) *Mat {
	m := &Mat{}
	for i := 0; i < L; i++ {
		for j := 0; j < L; j++ {
			m.a[i][j] = from.a[i][j]
		}
	}
	return m
}

func (m *Mat) Mul(other *Mat) *Mat {
	result := NewMat()
	for i := 0; i < L; i++ {
		for j := 0; j < L; j++ {
			for k := 0; k < L; k++ {
				result.a[i][j] = (result.a[i][j] + m.a[i][k]*other.a[k][j]) % MOD
			}
		}
	}
	return result
}

/* 单位矩阵 */
func I() *Mat {
	m := NewMat()
	for i := 0; i < L; i++ {
		m.a[i][i] = 1
	}
	return m
}

/* 矩阵快速幂 */
func quickMul(x *Mat, y int) *Mat {
	ans := I()
	cur := NewMatCopy(x)
	for y > 0 {
		if y&1 == 1 {
			ans = ans.Mul(cur)
		}
		cur = cur.Mul(cur)
		y >>= 1
	}
	return ans
}

func main() {
	s := "abcyy"
	t := 2
	nums := []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2}
	result := lengthAfterTransformations(s, t, nums)
	fmt.Println(result)
}

在这里插入图片描述

Rust完整代码如下:

const MOD: i64 = 1_000_000_007;
const L: usize = 26;

type Mat = [[i64; L]; L];

fn new_mat() -> Mat {
    [[0; L]; L]
}

fn identity() -> Mat {
    let mut m = new_mat();
    for i in 0..L {
        m[i][i] = 1;
    }
    m
}

fn mat_mul(a: &Mat, b: &Mat) -> Mat {
    let mut res = new_mat();
    for i in 0..L {
        for j in 0..L {
            let mut sum = 0;
            for k in 0..L {
                sum += a[i][k] * b[k][j];
            }
            res[i][j] = sum % MOD;
        }
    }
    res
}

fn mat_pow(mut base: Mat, mut exp: i64) -> Mat {
    let mut result = identity();
    while exp > 0 {
        if exp & 1 == 1 {
            result = mat_mul(&result, &base);
        }
        base = mat_mul(&base, &base);
        exp >>= 1;
    }
    result
}

fn length_after_transformations(s: &str, t: i64, nums: &[i32]) -> i64 {
    // 构造转换矩阵 T,T[i][j] = 1 表示从 j 变成 i 的可能路径
    let mut T = new_mat();
    for i in 0..L {
        for j in 1..=nums[i] {
            let to = (i + j as usize) % L;
            T[to][i] = 1;
        }
    }

    // 矩阵快速幂计算 T^t
    let res = mat_pow(T, t);

    // 统计初始字符串中各字母的数量
    let mut f = [0i64; L];
    for b in s.bytes() {
        f[(b - b'a') as usize] += 1;
    }

    // 计算最终长度 = ∑_(i,j) res[i][j] * f[j]
    let mut ans = 0i64;
    for i in 0..L {
        for j in 0..L {
            ans = (ans + res[i][j] * f[j]) % MOD;
        }
    }
    ans
}

fn main() {
    let s = "abcyy";
    let t = 2;
    let nums = [
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
    ];
    let result = length_after_transformations(s, t, &nums);
    println!("{}", result);
}

在这里插入图片描述

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。