📌  相关文章
📜  在给定约束下可以使用a,b和c形成的字符串数(1)

📅  最后修改于: 2023-12-03 14:51:32.988000             🧑  作者: Mango

题目描述

给定三个字符 'a'、'b' 和 'c',你需要计算在给定的约束条件下可以形成多少个长度为 n 且满足其中 任意连续三个字符不同 的字符串。

约束条件:

不允许出现连续三个字符不同的情况。换句话说,字符串中不能出现 "aba"、"abc"、"bac" 和 "cab",但是允许出现 "aa"、"bb" 和 "cc"。

示例 1:
输入: n = 3
输出: 19
解释: 可以生成的字符串是 "aaa"、"aab"、"aac"、"aba"、"abc"、"acb"、"acc"、"baa"、"bac"、"bbc"、"bca"、"bcb"、"bcc"、"caa"、"cab"、"cac"、"cba"、"cbc" 和 "cca"。
其中,字符串 "aab"、"abc"、"bac" 和 "cba" 不满足要求。
因为它们分别包含了连续三个字符 "aba"、"abc"、"bac" 和 "cab"。
解题思路

题目的关键是在任意连续三个字符不同的情况下,求符合要求的字符串数量。

显然,n=1时,只有1种情况,因为任何字符都符合条件;n=2时,每个字符有3种选择,即"a", "b", "c", 这样的字符串数量为3^2=9. 在n值较小的情况下,可以采用 "暴力" 枚举的方式来解决。但当 n 较大时,枚举所有的字符串就太浪费时间了。因为符合题意的字符串非常少,所以可以针对题目中的限制条件进行分析,提高算法效率。

我们可以定义两个数组: dp[n][0]: 表示长度为n的字符串,以a结尾,且符合条件的字符串的个数; dp[n][1]: 表示长度为n的字符串,以b结尾,且符合条件的字符串的个数; dp[n][2]: 表示长度为n的字符串,以c结尾,且符合条件的字符串的个数。

则记dp[n][0]、dp[n][1]、dp[n][2]分别是红色、蓝色、绿色三种箭头所指向的数字。当我们在长度为n-1的字符串末尾加上一个字符时:

对于新的字符串以 'a' 结尾的情况,如果之前的字符串以 'a' 结尾,则为以下两种情况的相加:

  • 保持不变(即在之前的字符串末尾加上一个 'a' );

  • 将之前的字符串末尾的 'b' 或 'c' 改成 'a',但是不能满足 “aba”、“abc”、“bac” 和 “cab” 四种情况。 对于新的字符串以 'a' 结尾的情况,如果之前的字符串以 'b' 结尾,则为以下一种情况:

  • 之前的字符串末尾加上一个 'a'。

同理,在新的字符串末尾加上 'b' 和 'c' 的情况也是类似的。这样下来,我们就得到了状态转移方程:

  • dp[n][0] = dp[n-1][0] + dp[n-1][1] + dp[n-1][2]
  • dp[n][1] = dp[n-1][0] + dp[n-1][2]
  • dp[n][2] = dp[n-1][0] + dp[n-1][1]

因此,只需要对前 n-1 阶段的状态进行处理,便可以得到第n阶段的所有状态。

最后,将处理完 n 阶段后三个到状态的和相加就是最终答案。由于状态转移方程的时间复杂度为 O(n),所以总时间复杂度可以做到O(n)。

代码示例
public int countVowelStrings(int n) {
    int[][] dp = new int[n + 1][5];
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= 5; j++) {
            if (i == 1) {
                dp[i][j] = 1;
            } else {
                for (int k = 1; k <= j; k++) {
                    dp[i][j] += dp[i - 1][k];
                }
            }
        }
    }
    int res = 0;
    for (int j = 1; j <= 5; j++) {
        res += dp[n][j];
    }
    return res;
}