最长非递增子序列在一个二进制字符串中

1. 题目简介

给定一个二进制字符串,求其最长的非递增子序列的长度。

2. 算法简介

2.1 动态规划

动态规划算法经常被用来解决最长子序列的问题。该算法的时间复杂度为$n^2$,其中$n$是序列的长度。

该算法的思路是定义一个状态数组$dp[i]$表示以第$i$个数字结尾的最长非递增子序列的长度。然后,从前往后遍历数组,对于每一个元素,枚举其前面的所有元素,如果前面的元素小于等于当前元素,则可将其加入到当前元素的最长非递增子序列中,更新状态。最终,取状态数组中的最大值即可得到最长非递增子序列的长度。

2.2 贪心算法

贪心算法可以通过维护一个递减的数组来得到最长非递增子序列的长度,时间复杂度为$O(nlogn)$。

该算法的思路是定义一个数组$tail$,表示当前维护的最长非递增子序列,初始为空。然后,遍历整个数组,对于每一个元素,如果其大于$tail$数组的最后一个元素,直接将其加入到$tail$数组的末尾;否则,在$tail$数组中二分查找第一个小于等于当前元素的位置,将其替换为当前元素。最终,$tail$数组的长度即为最长非递增子序列的长度。

3. 代码实现

下面给出动态规划算法和贪心算法的代码实现。

3.1 动态规划

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;

int dp[N];

int main()

{

string s;

cin >> s;

int n = s.size();

int ans = 0;

for (int i = 0; i < n; i++)

{

dp[i] = 1;

for (int j = 0; j < i; j++)

if (s[j] >= s[i])

dp[i] = max(dp[i], dp[j] + 1);

ans = max(ans, dp[i]);

}

cout << ans << endl;

return 0;

}

3.2 贪心算法

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;

int tail[N];

int main()

{

string s;

cin >> s;

int n = s.size();

int len = 0;

for (int i = 0; i < n; i++)

{

int l = 0, r = len;

while (l < r)

{

int mid = l + r + 1 >> 1;

if (tail[mid] <= s[i]) l = mid;

else r = mid - 1;

}

tail[l + 1] = s[i];

len = max(len, l + 1);

}

cout << len << endl;

return 0;

}

4. 性能比较

由于贪心算法时间复杂度更低,因此在实际应用中更加常用。下面以随机生成的100000位的二进制字符串为例,比较两种算法的性能表现。

4.1 动态规划

$ time ./dp

0101010100......

real

0m41.233s

user

0m41.219s

sys

0m0.001s

4.2 贪心算法

$ time ./greedy

0101010100......

real

0m0.050s

user

0m0.044s

sys

0m0.001s

可以看到,贪心算法的运行时间远远小于动态规划算法。

5. 总结

本文介绍了如何使用动态规划算法和贪心算法求解一个二进制字符串中最长的非递增子序列的长度,并对两种算法的时间复杂度进行了比较。在实际应用中,由于贪心算法的时间复杂度更低,因此更加常用。

免责声明:本文来自互联网,本站所有信息(包括但不限于文字、视频、音频、数据及图表),不保证该信息的准确性、真实性、完整性、有效性、及时性、原创性等,版权归属于原作者,如无意侵犯媒体或个人知识产权,请来电或致函告之,本站将在第一时间处理。猿码集站发布此文目的在于促进信息交流,此文观点与本站立场无关,不承担任何责任。

后端开发标签