字符串-最长不下降子序列

同样,也是在头条里碰到的题目之一。

一天,小凯同学震惊的发现,自己屋内的PM2.5指标是有规律的!小凯采样了PM2.5数值,发现PM2.5数值以小时为周期循环,即任意时刻的PM2.5总是和一个小时前相等!他的室友小文同学提出了这样一个问题,在t小时内的所有采样点中国,选区若干采样点的数值,能否找到一个PM2.5不曾下降过的序列?这个序列最长是多少?

输入:

第一行有两个整数n和t,表示每小时的采样点个数,和询问多少个时间的结果

第二行有n个整数,以空格分隔,表示一个小时内,每个采样点观测到的PM2.5数值

很明显的一题求最长不下降子序列的题目。 

优秀的O(nlogn)算法如下:

建立一个数组b[],其中b[i]表示长度为i的不下降序列中结尾元素的最小值,用len表示数组当前最长长度,算法完成后k的值即为最长不下降子序列的长度

假设,当前已求出长度为k,下一步判断a[i]和b[k]

  1. 如果a[i]>=b[k],即a[i]大于长度为k的序列中的最后一个元素,因此,长度可加1,b[k++]=a[i];
  2. 如果a[i]<b[k],则在0-k中找出第一个大于a[i]的数,并用a[i]替换之,很明显,用a[i]可以找到更长的长度
  3. 其中,寻找第一个大于a[i]的数可用二分查找,复杂度为O(logn),故整体的算法复杂度为O(nlogn)
vector<int> LLPM(n, 0); //初始化 //PM为原数组
LLPM[0] = PM[0];
int len = 1;
for( int i=1; i<n; ++i ){
    int j = i;
    if( PM[j] >= LLPM[len-1] ){ //当下一位大于LLPM[len-1],则在后面添加一位
        LLPM[len++]=PM[j];
    }
    else{    //若下一位小于,则找到前len个中第一个比它大的数,并替换掉
        auto k=upper_bound(LLPM.begin(), LLPM.begin()+len, PM[j]); //二分查找函数,找到第一个比它大的数
        *k = PM[j];
    }
}

输入参考例子

9 3 7 5 9 3 7 5 9 3 7 5

 937593759375
a[0]93333333333
a[1]007553333333
a[2]000099755333
a[3]000000009975
a[4]000000000000

 由此可得出,最长不下降长度为4

以下是全部代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(){
    int n, t; //采样点n,小时t
    cin >> n >> t;
    vector<int> PM(n);
    for(int i=0; i<n; ++i){
        cin >> PM[i];
    }
    //寻找最长子串
    vector<int> LLPM(n*t, 0); //初始化
    LLPM[0] = PM[0];
    int len = 1;
    for( int i=1; i<n*t; ++i ){
        int j = i%n;
        if( PM[j] >= LLPM[len-1] ){ //当下一位大于LLPM[len-1],则在后面添加一位
            LLPM[len++]=PM[j];
            cout << "将" << PM[j] << "加到第" << len << "位    ";
            for( int i=0; i<n*t; ++i )
                cout << LLPM[i] << " ";
            cout << endl;
        }
        else{    //若下一位小于,则找到前len个中第一个比它大的数,并替换掉
            auto k=upper_bound(LLPM.begin(), LLPM.begin()+len, PM[j]); //二分查找函数,找到第一个比它大的数
            *k = PM[j];
            cout << "用" << PM[j] << "替换第" << k-LLPM.begin() << "位"<< "    ";
            for( int i=0; i<n*t; ++i )
                cout << LLPM[i] << " ";
            cout << endl;
        }
    }
    cout << "长度为:" << len << endl;
    return 0;
}
4 3
9 3 7 5
用3替换第0位    3 0 0 0 0 0 0 0 0 0 0 0
将7加到第2位    3 7 0 0 0 0 0 0 0 0 0 0
用5替换第1位    3 5 0 0 0 0 0 0 0 0 0 0
将9加到第3位    3 5 9 0 0 0 0 0 0 0 0 0
用3替换第1位    3 3 9 0 0 0 0 0 0 0 0 0
用7替换第2位    3 3 7 0 0 0 0 0 0 0 0 0
用5替换第2位    3 3 5 0 0 0 0 0 0 0 0 0
将9加到第4位    3 3 5 9 0 0 0 0 0 0 0 0
用3替换第2位    3 3 3 9 0 0 0 0 0 0 0 0
用7替换第3位    3 3 3 7 0 0 0 0 0 0 0 0
用5替换第3位    3 3 3 5 0 0 0 0 0 0 0 0
长度为:4

结果刚好验证了以上思想。最长不下降子序列打卡!

此外,对于最长递增、最长递减、最长不增加等子序列,只需要 修改其中的判断条件即可。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值