题目陈述

大意:给定一个字符串仅由'5'、'2' 、'0' 和‘?’组成,其中'?'可以代表任何一个字符,所有可能的字符串中,位置不同的为 "520" 的子序列共有多少个?

算法一:朴素算法

算法思路

  • 最暴力的想法,直接枚举“520”的所有可能
  • 第一层循环枚举5和?,第二层枚举2和?,第三层循环枚举0和?
  • 当然,此处枚举时,要注意'2'的位置一定得在‘5’的后面,‘0’得在‘2’的后面

    代码实现

    typedef long long LL; //简化声明定义long long 
    class Solution
    {
    public:
      LL a, b, c, md = 998244353;
      int findOccurrences(string s)
      {
          LL ans = 0; //答案
          int len = s.size(); // 字符串长度
          for (int i = 0; i < len ; i ++ )
              if(s[i] == '5' || s[i] == '?') //找到“5”
                  for (int j  = i + 1; j < len; j ++ )
                      if(s[j] == '2' || s[j] == '?') //找到“52”     
                          for(int k = j + 1; k < len; k ++ )
                              if(s[k] == '0' || s[k] == '?') // 找到“520”
                                  ans = (ans + 1) % md; //答案过大取模
          return ans;
      }
    };

    复杂度分析

  • 时间复杂度,三层枚举循环,为
  • 空间复杂度,定义了变量,为

    算法二:动态规划

    算法思路

  • 通过上面的暴力做法,我们不难发现
  • “520”序列的前提是“52”序列,“52”序列的前提是“5”序列
  • 所以我们不难设计出状态
  • 代表前个字符中,所有可能的字符串中,位置不同的为 "5" 的子序列的个数
  • 代表前个字符中,所有可能的字符串中,位置不同的为 "52" 的子序列的个数
  • 代表前个字符中,所有可能的字符串中,位置不同的为 "520" 的子序列的个数
  • 如果当前位置字符,则说明“5”的子序列个数增加了一个,
  • 如果当前位置字符,则可以将之前的个“5”序列全部变成“52”序列,再加上之前有的,总共有
  • 如果当前位置字符,则可以将之前的个“52”序列全部变成“520”序列,再加上之前有的,总共有
  • 如果当前位置字符,则说明它可以被替换成上面三种情况,将上面三种的转移方程照抄即可。

    空间优化

  • 不难发现,当前位置的状态只跟前一个位置有关
    图片说明
  • 所以,对于一种序列的维护,我们只需要用一个变量即可
  • 将空间复杂度从优化到了
  • 此时注意处的转移,设之前的分别对应,则正确的转移顺序应该为
    在这里插入图片描述
  • 如果转移顺序为,则会导致以下的后果,从而导致错误的结果
    在这里插入图片描述

代码实现

typedef long long LL;
class Solution
{
public:
    LL a, b, c, md = 998244353;
    int findOccurrences(string s)
    {
        a = b = c = 0; //“5”,“52”,“520”序列的方案数量
        int len = s.size(); //字符串长度
        s = ' ' + s; //加一,省去判断s[0]的情况,让编号从1开始
        //方便编码
        for (int i = 1; i <= len; i++)
        {
            if (s[i] == '5') //子序列为“5”的状态转移
                a++;
            else if (s[i] == '2') //子序列为“52”的
                b += a;
            else if (s[i] == '0') //子序列为“520”的
                c += b;
            else if (s[i] == '?') //可以转移到上面三种状态
            {
                c += b; //此处三行不能互相调换
                b += a; //因为三种情况都是从前一个位置转移过来
                //如果a或b先转移,就得到了当前位置的a或b,那么b或c的方程就会出现问题
                a++;
            }
            a %= md, b %= md, c %= md; //答案过大取模
        }
        return c; //返回的即"520"序列的数目
    }
};

复杂度分析

  • 时间复杂度,状态转移方程递推为
  • 空间复杂度,定义了变量,