问题描述:
小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时:
当i+1==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).