找出最长的神奇数列
问题描述
小F是一个好学的中学生,今天他学习了数列的概念。他在纸上写下了一个由 0 和 1 组成的正整数序列,长度为 n。这个序列中的 1 和 0 交替出现,且至少由 3 个连续的 0 和 1 组成的部分数列称为「神奇数列」。例如,10101 是一个神奇数列,而 1011 不是。现在,小F想知道在这个序列中,最长的「神奇数列」是哪一个。如果有多个神奇数列,那么输出最先出现的一个。
测试样例
样例1:
输入:
inp = "0101011101"
输出:'010101'
样例2:
输入:
inp = "1110101010000"
输出:'10101010'
样例3:
输入:
inp = "1010101010101010"
输出:'1010101010101010'
分析问题
- 在一个由0和1组成的数列中,找出一个最长子串。子串要求:01交替出现,长度大于等于3.
- 如果有多个神奇数列,那么输出最先出现的一个。则当另一个子串长度大于已找出子串时,进行替换。
算法逻辑
- 初始化一个空字符串
out来存储最长的01交替子串。 - 使用一个
while循环遍历输入字符串inp,直到倒数第二个字符,因为需要比较当前字符和下一个字符。 - 在循环内部,检查当前字符
inp[i]和下一个字符inp[i + 1]是否不同(即是否形成01交替)。 - 如果是01交替的开始,记录起始位置
start和交替子串的长度length。 - 继续遍历,扩展这个交替子串,直到遇到不满足01交替的字符或者到达字符串末尾。
- 在每次扩展后,检查当前交替子串的长度是否比
out中存储的子串长,如果是,则更新out。 - 继续遍历直到处理完所有字符。
- 如果找到的最长交替子串长度小于 3,返回空字符串。 否则,返回找到的最长交替子串。
时间复杂度分析
-
外层循环:
while (i < inp.length() - 1):这个循环会遍历整个字符串inp,因此它的复杂度是O(n),其中n是字符串的长度。
-
内层循环:
while (i + 1 < inp.length() && inp[i] != inp[i + 1]):这个循环会在检测到交替子串时执行,直到交替子串结束。- 在最坏情况下,这个循环可能会遍历整个字符串
inp,但每个字符只会被访问一次。
-
字符串操作:
inp.substr(start, length):这个操作的时间复杂度是O(length),但在最坏情况下,length可能接近n。
综合分析
- 外层循环的复杂度是
O(n)。 - 内层循环在最坏情况下也会遍历整个字符串,但由于每个字符只会被访问一次,所以内层循环的复杂度也是
O(n)。 - 字符串操作的复杂度在最坏情况下是
O(n)。
因此,整个算法的时间复杂度是 O(n)。
代码
#include <iostream>
#include <string>
std::string solution(const std::string &inp) {
std::string out = "";
int i = 0;
while (i < inp.length() - 1) {
// 检查是否是01交替子串的开始
if (inp[i] != inp[i + 1]) {
int start = i;
int length = 1; // 至少包含一个交替
// 扩展这个交替子串,直到不再是01交替
while (i + 1 < inp.length() && inp[i] != inp[i + 1]) {
i++;
length++;
}
// 如果找到更长的交替子串,更新结果
if (length > out.length()) {
out = inp.substr(start, length);
}
}
i++;
}
if (out.length() < 3)
return "";
return out;
}
总结
此方法采用线性扫描,算法的核心是遍历整个序列,识别出所有01交替的子串,并记录下最长的一个。 当前算法的时间复杂度已经是O(n),这意味着我们已经在单次遍历中找到了最长的神奇数列。在这种情况下,O(n)是最佳的可能时间复杂度,因为我们至少需要查看序列中的每个字符一次以确定最长的神奇数列。