C++题解:找出最长的神奇数列| 豆包MarsCode AI 刷题

182 阅读4分钟

问题描述:

小F是一个好学的中学生,今天他学习了数列的概念。他在纸上写下了一个由 0 和 1 组成的正整数序列,长度为 n。这个序列中的 1 和 0 交替出现,且至少由 3 个连续的 0 和 1 组成的部分数列称为「神奇数列」。例如,10101 是一个神奇数列,而 1011 不是。现在,小F想知道在这个序列中,最长的「神奇数列」是哪一个。你能帮他找到吗?

如果有多个神奇数列,那么输出最先出现的一个。

测试样例:

样例1:

输入:inp = "0101011101"
输出:'010101'

样例2:

输入:inp = "1110101010000"
输出:'10101010'

样例3:

输入:inp = "1010101010101010"
输出:'1010101010101010'

解题思路:

我们可以使用动态规划的思路来解决这道题目。

除开长度刚好为3的情况,一个神奇数列一定是由另一个神奇数列组成的。 对于一个下标从i到j的序列来说,它一定是由下标i到j-1的序列和下标为j的字符组成。 如果i到j-1的序列为神奇序列,那么我们只需要判断下标j的字符和下标j-1的字符是否不同即可。如果相同则代表这个序列一定不是神奇序列。 我们设f(i,j)为下标i到j组成的序列是否是神奇序列,也就是说f(i,j)要么是0要么是1.

我们可以得到如下转移方程:

当i+1<j时: f(i,j)=f(i,j1)inp[j1]inp[j] f(i,j) = f(i,j-1)\cap inp[j-1]\neq inp[j]

当i+1==j时: f(i,j)=inp[j1]inp[j] f(i,j) = inp[j-1]\neq inp[j]

题目要求求出最长的神奇序列,所以我们需要在循环中更新当前的最长神奇序列。 也就是说如果当前这个序列是神奇序列的话,求出其长度并与当前最大长度比较。并同时维护答案序列。

c++代码如下:

代码:

其实在这个版本中,我们可以看出第一维度是完全没必要的,只是从j-1的维度转移到j。那么我们可以将dp数组优化为一维。


std::string solution(const std::string& inp) {
    // Edit your code here
    //动态规划
    //dp[i][j]表示i-j的序列是不是神奇数列
    int n = inp.size();
    int res=0;
    string str;
    vector<vector<int>> dp(n,vector<int>(n,0));
    for(int i=0;i<n;++i){
        for(int j=i;j<n;++j){
            if(i+1==j){
                dp[i][j]= inp[i]!=inp[j];
            }else if(i+1<j){
                dp[i][j] = dp[i][j-1]&(inp[j-1]!=inp[j]);
            }
            if(dp[i][j]&&j-i+1>=3){
                if(j-i+1>res){
                    str = inp.substr(i,j-i+1);
                    res=j-i+1;
                }
            }
        }
    }
    return str;
}

这个版本将dp数组转为一维数组,并且将代码利用豆包的MarsCode转为java代码(由于本人是CPP选手,目前在学习java中,刷题都是先写c++代码,然后利用豆包转为java再提交的).

public static String solution(String inp) {
        int n = inp.length();
        int res = 0;
        String str = "";
        int[] dp = new int[n];
        Arrays.fill(dp, 0);

        for (int i = 0; i < n; ++i) {
            for (int j = i; j < n; ++j) {
                if (i + 1 == j) {
                    dp[j] = inp.charAt(i) != inp.charAt(j) ? 1 : 0;
                } else if (i + 1 < j) {
                    dp[j] = dp[j - 1] & (inp.charAt(j - 1) != inp.charAt(j) ? 1 : 0);
                }
                if (dp[j] != 0 && j - i + 1 >= 3) {
                    if (j - i + 1 > res) {
                        str = inp.substring(i, j + 1);
                        res = j - i + 1;
                    }
                }
            }
        }
        return str;
    }

其实这个代码还有优化的空间,我们可以看出dp[j]只是由dp[j-1]转移而来的,也就是说我们只需要两个变量就可以完成状态转移了。此外不需要额外定义一个字符串用来返回。

public static String solution(String inp) {
        int n = inp.length();
        int res = 0;
        int start=0;
        int odp=0;
        int ndp=0;
        for (int i = 0; i < n; ++i) {
            for (int j = i; j < n; ++j) {
                if (i + 1 == j) {
                    ndp = inp.charAt(i) != inp.charAt(j) ? 1 : 0;
                } else if (i + 1 < j) {
                    ndp = odp & (inp.charAt(j - 1) != inp.charAt(j) ? 1 : 0);
                }
                if (ndp != 0 && j - i + 1 >= 3) {
                    if (j - i + 1 > res) {
                        start = i;
                        res = j - i + 1;
                    }
                }
                odp=ndp;
            }
        }
        return inp.substring(start, res+start);
    }

算法复杂度:

空间复杂度:由于只用了常数级别的变量,所以空间复杂度是O(1).

时间复杂度:由于是两个for循环,那么时间复杂度为O(n^2).