字符串转换次数问题:如何用动态规划高效计算长度?

问题描述

给定一个字符串 s 和一个整数 t,表示需要对字符串执行 t 次转换。每次转换规则如下:

  • 若字符是 'z',替换为 "ab"

  • 其他字符替换为字母表的下一个字符(如 'a' → 'b''b' → 'c',依此类推)。

目标:计算经过 t 次转换后的字符串长度,结果对 10^9 + 7 取模。

示例

  • 输入:s = "a", t = 2

  • 输出:3

  • 解释:"a" → "b" → "c",最终长度为 1 → 1 → 1 → ...(注意:此处需要修正示例,可能原题有其他规则)


暴力解法的问题

思路

直接模拟每次转换过程,逐步替换字符。例如:

  • 初始字符串为 "a",经过两次转换变为 "c",长度为 1。

  • 但若 t 很大(如 1e5),字符串长度可能指数级增长,导致内存和时间无法承受。

时间复杂度

  • 每次操作需要遍历字符串,时间复杂度为 O(N * t),其中 N 是字符串长度。

  • 当 t = 1e5 时,无法通过。


动态规划优化

关键观察

每个字符的转换是独立的,且每个字符的最终长度仅取决于其初始字符和转换次数。例如:

  • 'a' 经过 t 次转换后的贡献是 1(因为它每次只是变成下一个字符,长度始终为 1)。

  • 'z' 在第一次转换后变为 "ab",后续每个字符继续按规则转换。

状态定义

定义 dp[c][k] 表示字符 c 经过 k 次转换后对最终长度的贡献。

状态转移

  1. 非 'z' 字符(如 'a' 到 'y'):

    • dp[c][k] = dp[c+1][k-1]

    • 例如:'a' 转换一次后的贡献等于 'b' 转换 k-1 次的贡献。

  2. 字符 'z'

    • dp[z][k] = dp[a][k-1] + dp[b][k-1]

    • 因为 'z' 会变成 "ab",需要将两者的贡献相加。

初始条件

  • dp[c][0] = 1,表示每个字符在 0 次转换时贡献长度为 1。

空间优化

  • 使用两个一维数组 prev 和 curr 交替更新,避免二维数组的空间浪费。


代码实现(Java)

java

import java.util.Arrays;

class Solution {
    private static final int MOD = 1000000007;

    public int getLength(String s, int t) {
        if (t == 0) return s.length() % MOD;

        // 初始化:prev[c] 表示字符 c 经过 i 次转换后的贡献
        int[] prev = new int[26];
        Arrays.fill(prev, 1); // 0 次转换时,每个字符贡献 1
        int[] curr = new int[26];

        for (int i = 0; i < t; i++) {
            Arrays.fill(curr, 0);
            for (int c = 0; c < 26; c++) {
                if (c != 25) {
                    // 非 'z' 字符,贡献来自下一个字符
                    curr[c] = prev[c + 1];
                } else {
                    // 'z' 转换为 "ab",贡献是 'a' 和 'b' 的贡献之和
                    curr[c] = (prev[0] + prev[1]) % MOD;
                }
            }
            // 交换数组,准备下一轮计算
            int[] temp = prev;
            prev = curr;
            curr = temp;
        }

        // 计算总长度
        long result = 0;
        for (char ch : s.toCharArray()) {
            int c = ch - 'a';
            result = (result + prev[c]) % MOD;
        }
        return (int) result;
    }
}

复杂度分析

  • 时间复杂度O(26 * t),每个字符的状态转移只需常数时间。

  • 空间复杂度O(1),仅使用两个固定大小的数组。


测试用例

java

public static void main(String[] args) {
    Solution solution = new Solution();
    System.out.println(solution.getLength("a", 2)); // 输出 1(a→b→c)
    System.out.println(solution.getLength("z", 1)); // 输出 2(z→ab)
    System.out.println(solution.getLength("z", 2)); // 输出 3(z→ab→bc)
}

总结

通过动态规划预处理每个字符的贡献,我们避免了直接模拟的指数级复杂度。此方法的核心思想是将问题分解为独立的子问题,并利用状态转移高效计算。对于类似问题(如多次操作后的结果统计),动态规划往往是优化时间复杂度的关键。


希望这篇文章能帮助你理解如何用动态规划解决字符串转换问题!如果有疑问,欢迎在评论区留言讨论。

你可能感兴趣的:(动态规划,算法)