可爱串

361 阅读3分钟

题目

小红定义一个字符串是可爱串,当且仅当该字符串包含子序列"red",且不包含子串"red"。我们定义子序列为字符串中可以不连续的一段,而子串则必须连续。例如rderd包含子序列"red",且不包含子串"red",因此该字符串为可爱串。小红想知道,长度为n的、仅由r、e、d三种字母组成的字符串中,有多少是可爱串? 答案请对10^9+7取模。数据范围:1≤n≤10^5

分析

设法求得所有包含子序列red(可连续)的方案数 A 和包含子串red的方案数 B,A - B 即为答案。

  1. 首先求 A:
  • 假设 f(i) 表示长度为 i 的字符串中包含 red 子序列的方案数。f(i) = f(i-1) * 3 + g(i-1)

    • 若该字符串以 r 或者 e 结尾,则 red 子序列在前 i - 1 个字母中,方案数为 f(i-1) * 2

    • 若该字符串以 d 结尾,可分为以下两种情况

      • 这个d不构成子序列 red 的一部分,即前 i - 1 个字母中存在 red 子序列,方案数为 f(i-1)

      • 这个 d 必须构成子序列 red 的一部分,即前 i - 1 个字母中只有re子序列,没有 red

  1. 所以现在需要求长度为 i 的字符串中包含 re 子序列但不包含 red 的方案数,设其为 g(i)。
  • g(i) 表示长度为 i 的字符串中包含 re 子序列的方案数。g(i) = g(i-1) * 2 + 1

    • 若该字符串以 d 结尾,而且前 i - 1个字母中存在 re 子序列,则必然构成 red 子序列,故此情况不存在

    • 若该字符串以 r 结尾,则 re 子序列在前 i - 1 个字母中,方案数为 g(i-1)

    • 若该字符串以 e 结尾,可分为以下两种情况

      • 这个 e 不构成子序列 re 的一部分,即前 i - 1 个字母中存在 re 子序列,方案数为 g(i-1)

      • 这个 e 必须构成子序列 re 的一部分,即前 i - 1 个字母中所有的 r 排在 e (if exist)的后面 ,r 有 i - 1个位置可以放,r的右边只能放置 r d,r的左边只能放置 e d 。所以方案数 = (i - 1) * 2^(i - 2)

  1. 求 B:
  • h(i) 表示长度为 i 的字符串中包含 red 子串的方案数。h(i) = 3 ^(i - 3) + h(i-1) * 3 - h(i-3)

    • 若该字符串以 red 结尾,则前 i - 3 个字符可任意排列,方案数 = 3 ^(i - 3)

    • 若该字符串不以 red 结尾,可分为以下两种情况

      • 不以 d 结尾,则前 i - 1 个字母中存在 red 子串,方案数 = h(i-1) * 2

      • 以 d 结尾,则前 i - 1 个字母中存在 red 子串 并且倒数两位不能是 re。所以方案数是 h(i-1) - h(i-3)

  1. 综上 A - B:f(n) - h(n)。

代码

#include <cstdio>
#include <vector>
#include <cmath>



int main() {
    int n;
    scanf("%d", &n);
    std::vector<int> f(n + 1), g(n + 1), h(n + 1);
    
    for (int i = 2; i <= n; i ++) g[i] = g[i-1] * 2 + (i - 1) * pow(2, i - 2);
    
    for (int i = 3; i <= n; i ++) f[i] = f[i-1] * 3 + g[i-1];
    
    for (int i = 3; i <= n; i ++) h[i] = pow(3, i - 3) + h[i-1] * 3 - h[i-3];
    
    printf("%d %d %d", f[n], h[n], f[n] - h[n]);
}