LeetCode每日一题2025-05-13
3335. 字符串转换后的长度 I M
给你一个字符串 s 和一个整数 t,表示要执行的 转换 次数。每次 转换 需要根据以下规则替换字符串 s 中的每个字符:
- 如果字符是
'z',则将其替换为字符串"ab"。 - 否则,将其替换为字母表中的下一个字符。例如,
'a'替换为'b','b'替换为'c',依此类推。
返回 恰好 执行 t 次转换后得到的字符串的 长度。
由于答案可能非常大,返回其对 10⁹ + 7 取余的结果。
示例 1:
输入: s = “abcyy”, t = 2
输出: 7
解释:
- 第一次转换 (t = 1)
'a'变为'b''b'变为'c''c'变为'd''y'变为'z''y'变为'z'- 第一次转换后的字符串为:
"bcdzz"- 第二次转换 (t = 2)
'b'变为'c''c'变为'd''d'变为'e''z'变为"ab"'z'变为"ab"- 第二次转换后的字符串为:
"cdeabab"- 最终字符串长度:字符串为
"cdeabab",长度为 7 个字符。
示例 2:
输入: s = “azbk”, t = 1
输出: 5
解释:
- 第一次转换 (t = 1)
'a'变为'b''z'变为"ab"'b'变为'c''k'变为'l'- 第一次转换后的字符串为:
"babcl"- 最终字符串长度:字符串为
"babcl",长度为 5 个字符。
提示:
1 <= s.length <= 10⁵s仅由小写英文字母组成。1 <= t <= 10⁵
问题分析
对于任意单个字符 c,定义函数
那么对整个字符串 s,最终答案就是
算法思路
状态转移
边界:(不变长度为 1)
若 ,则每次转换就是下一个字母
若 ,则“ z ”展开为 “ab”,对应长度之和
我们可以对所有 26 个字母维护一个长度-26 的数组 dp,其中dp[i] 表示在当前步剩余转换次数 时,字母 char('a'+i) 对应的最终长度。
初始化:dp[i]=1(即 )。
对每一次转换 到 :
- 对
i=0…24(即a…y),新的dp_new[i] = dp[i+1]; - 对
i=25(即z),dp_new[25] = (dp[0] + dp[1]) % MOD。 - 然后将
dp_new赋回dp。
最后,对字符串 s 中每个字符累加对应的 dp[index] 并取模即为答案。
时间复杂度
时间:每步更新 ,共做 步,外加对长度 的一次遍历,故整体 。
空间:只使用常数大小的长度 26 数组,故 。
代码实现
1 | class Solution: |
另看到题解:预处理 + 动态规划(DP) :
状态定义
数组 f 长度为 ,其中前 位初始化为 1(表示任何字母在 0 步转换时长度为 1),后面用来存储更大步数下的长度。
状态转移
对于下标 ,f[i+26] = (f[i] + f[i+1]) % \text{MOD},恰好对应了:
- 当你对
'z'(在前 26 中的最后一个)做一次转换,它会变成"ab",长度就是前面两个状态的和。 - 而对非
'z'的字母,其实就是“下一个字母”的长度,也可以映射到同样的数组偏移关系里。
预处理好所有可能的值
因为 ,预先把 f[26] 到 f[26 + 10^5] 都算好,这样每次调用 lengthAfterTransformations(s, t) 时,只要对串中每个字符 取 f[t + (ord(c)-ord('a'))],再累加取模即可。
当 接近上限 时,26*t 约为 次小操作;而打表只做 次,常数更小,大约快 20× 以上。
且打表后,不管用户输入的 是多少(只要 ),都能直接 拿到每个字符的长度贡献。
1 | MOD = 1_000_000_007 |
