《神奇字符串构造问题》和《数组子序列的排列数量》| 豆包MarsCode AI刷题

97 阅读4分钟

今天我们将在豆包MarsCode AI刷题平台上,完成《神奇字符串构造问题》与《数组子序列的排列数量》这两个算法问题,通过这些练习提升用户解决此类问题的能力

《神奇字符串构造问题》题面如下:

image.png

问题理解

我们需要将一个字符串转换为“神奇字符串”,即满足以下两个条件:

  1. 字符串的长度是3的倍数。
  2. 对于任意的下标 i,其中 i 是3的倍数(即 i = 3x),都必须满足 S[i] = S[i+1] = S[i+2]

解题思路

  1. 动态规划:通过动态规划来解决这个问题。动态规划的核心思想是将问题分解为子问题,并通过存储子问题的解来避免重复计算。

  2. 状态定义

    • f[i][j][k] 表示前 i 个字符中,以字符 j 结尾,且长度模3余 k 的最大子序列长度。
    • i 是字符串的下标,j 是字符('a' 到 'z'),k 是0, 1, 2(表示长度模3的余数)。
    • 这里我们采用滚动数组优化,对于第一个维度只保留遍历到前一个字符和当前字符两个时期的结果,i 分别取值0和1
  3. 状态转移

    • 对于以字符 c结尾且长度模3余1的情况,我们尝试将其加入到以 prec 结尾且长度模3余0的子序列中,prec 为所有可能的字符,更新 f[1][c - 'a'][1]
    • 更新 f[1][c - 'a'][2]f[1][c - 'a'][0],分别表示长度模3余2和0的情况。
  4. 最终结果

    • 遍历所有字符 j,找到 f[0][j][0] 的最大值,表示以 j 结尾且长度为3的倍数的最大子序列长度。
    • 最终结果是 n - f[0][j][2],即字符串总长度减去能够得到的最长神奇字符串,得到需要删除的最少字符数。

具体实现

int solution(string S) {
    // write code here
    int n = S.size();
    int ret = INT_MAX;
    int f[2][26][3];
    memset(f,0,sizeof(f));
    for(char c : S){
        for(char prec='a';prec<='z';prec++){
            f[1][c - 'a'][1] = max({f[1][c - 'a'][1], f[0][c - 'a'][1], f[0][prec - 'a'][0]+1});
        }
        f[1][c - 'a'][2] = max(f[0][c - 'a'][2], f[0][c - 'a'][1]>=1 ? f[0][c - 'a'][1]+1 : 0);
        f[1][c - 'a'][0] = max(f[0][c - 'a'][0], f[0][c - 'a'][2]>=2 ? f[0][c - 'a'][2]+1 : 0);
        // 滚动数组
        for(int i=0;i<26;i++){
            for(int j=0;j<3;j++){
                f[0][i][j] = f[1][i][j];
            }
        }
    }
    for(int i=0;i<26;i++){
        ret = min(ret, n - f[0][i][0]);
    }
    return ret;
}

《数组子序列的排列数量》题面如下:

image.png

题目理解

题目要求我们计算一个数组中有多少个子序列是一个排列。具体来说,子序列中的元素必须是由1到m的一个完整排列,并且必须按升序排列(注意这里题目描述有些问题,根据下面的测试用例来看的话,子序列的元素并不需要按照升序排列,这个升序应该是指1到m的排列顺序是升序的)。

数据结构选择

使用一个长度为maxval的数组f来记录每个数字的排列情况,maxval表示a中最大值,f[i]表示以数字i结尾的排列数量。

算法步骤

  1. 排序数组:首先对数组a进行了排序,这是为了确保后续处理时能够按升序处理数字。
  2. 初始化数组f:将f[0]初始化为1,表示空排列的情况。
  3. 动态规划更新f:遍历数组a中的每个数字i,并更新f[i]f[i-1]的值。这一步的目的是计算以当前数字i结尾的排列数量。
  4. 累加结果:最后,遍历数组f,将所有1 ≤ i ≤ maxvalf[i]累加到ret中,得到最终的结果。

具体实现

int solution(int n, std::vector<int> a) {
    // write code here
    int ret=0;
    sort(a.begin(), a.end());
    int maxval = a.back();
    vector<int> f(maxval+1,0);
    f[0] = 1;
    for(int i: a){
        f[i] += f[i-1];
    }
    for(int i=1;i<=maxval;i++){
        ret += f[i];
    }
    return ret;
}

借助豆包MarsCode AI刷题平台,我们不仅高效地解决了《神奇字符串构造问题》和《数组子序列的排列数量》,还加深了对相关算法和数据结构的理解,后续会借助豆包MarsCode AI给大家展示更多题目的解法。