3510. 最长公共子序列

这篇博客探讨了如何将最长公共子序列问题转化为最长上升子序列,当其中一个序列元素不重复时。还介绍了如何计算两个楼房编号之间的最短移动距离,以及解决日期解析和日志统计的问题。通过实例和代码解释了各种算法的实现,包括二分查找、曼哈顿距离和滑动窗口等技术。
摘要由CSDN通过智能技术生成

3510. 最长公共子序列

给出两个长度为 n 的整数序列,求它们的最长公共子序列(LCS)的长度,保证第一个序列中所有元素都不重复。注意:第一个序列中的所有元素均不重复。
第二个序列中可能有重复元素。
一个序列中的某些元素可能不在另一个序列中出现。
输入格式
第一行包含一个整数 n。接下来两行,每行包含 n 个整数,表示一个整数序列。
输出格式
输出一个整数,表示最长公共子序列的长度。数据范围
1≤n≤106,
序列内元素取值范围 [1,106]。
在这里插入图片描述

定理:最长公共子序列转化成最长上升子序列,只需要其中任意一个序列数元素不重复。
在这里插入图片描述

/*最长公共子序列转化成上升子序列只需要任意一个序列不重复即可。
思路:转化为最长公共子序列,因为不重复,都有唯一一个对应的坐标q[i]
二分找出一个比x小的最大元素的下标,按照要求插入到它的后面。

思路
已知第一个序列元素唯一,第二个序列元素可能重复
-可得第二个序列元素在第一个序列中元素下标一定唯一不变。
·所以可以将两个数组最长公共子序列转化为最长上升子序列。子序列定义为:组成子序列元素在原数组的下标一定是严格单调递增的。

#include <bits/stdc++.h>
using namespace std;

const int N=1E6+10;
int  a[N],idx[N],b[N];

int main(){
    int n;
   scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
     scanf("%d",&a[i]);
        idx[a[i]] = i;
    }
    int x;
    for(int i =1;i<=n;i++){
       scanf("%d",&x);
       b[i] =idx[x];
    }
    vector<int>res;
    
    for(int i=1;i<=n;i++)
    {
        if(b[i]==0) continue;
        if(res.empty()||res.back()<b[i]) res.push_back(b[i]);
        else {
            int id =lower_bound(res.begin(),res.end(),b[i])-res.begin();
            res[id] =b[i];
        }
    }
    printf("%d",res.size());
    return 0;
}

移动距离

X星球居民小区的楼房全是一样的,并且按矩阵样式排列。

其楼房的编号为 1,2,3… 当排满一行时,从下一行相邻的楼往反方向排号。

比如:当小区排号宽度为 6 时,开始情形如下:

1 2 3 4 5 6 12 11 10 9 8 7 13 14 15 … 我们的问题是:已知了两个楼号 m 和
n,需要求出它们之间的最短移动距离(不能斜线方向移动)。

//将m和n分别映射从0开始 便于计算 行列 并且下标为奇数的时候要对列进行翻转
#include <bits/stdc++.h>
using namespace std;
int main(){
    int w,n,m;
    scanf("%d%d%d",&w,&m,&n);
    
    m--; n--;  //讲两个楼的行号和列号从下标为0开始映射 (便于计算楼层数)
    
    int x1 = m/w; int x2 = n/w;   //计算行号 
    int y1 = m%w;  int y2 = n%w;   //模w计算列号 下标从0开始
    
     if(x1%2) y1 = w-1-y1;   //如果列号为奇数就表示是从右往左 所以进行列号翻转
     if(x2%2) y2 = w-1-y2; //如果列号为奇数就表示是从右往左 所以进行列号翻转
    
     cout<<abs(x1-x2)+abs(y1-y2);  //使用曼哈顿距离计算长度
    
}

日期问题

小明知道这些日期都在1960年1月1日至2059年12月31日。

令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。

更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。

比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。

给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?

输入格式 一个日期,格式是”AA/BB/CC”。

即每个’/’隔开的部分由两个 0-9 之间的数字(不一定相同)组成。

输出格式 输出若干个不相同的日期,每个日期一行,格式是”yyyy-MM-dd”。

多个日期按从早到晚排列。

数据范围 0≤A,B,C≤9 输入样例: 02/03/04 输出样例: 2002-03-04 2004-02-03 2004-03-02

**思路:**枚举区间年份中的日期,首先判断每个日期是否合法,如果合法的话就根据条件判断(格式为年月日、月日年、日月年 分别与输入的三个数字相等)就输出。

#include <bits/stdc++.h>
using namespace std;
int months[13] ={0,31,28,31,30,31,30,31,31,30,31,30,31};

bool check_valid(int year,int month,int day){
    if(month==0 ||month>12) return false;
    if(day==0) return false;
    if(month!=2&&day>months[month]) return false;
    else {
         int leap = (year%4==0&&year%100!=0)||(year%400==0);
         if(day>months[month]+leap) return false;
    }
    return true;
}

int main(){
    int a,b,c;
  scanf("%d/%d/%d",&a,&b,&c);
    for(int date = 19600101;date <=20591213; date++){   //遍历区间年份 判断合法年份再进行判断
         int year = date/10000, month = date%10000/100, day = date %100;  //算出年月日
         if(check_valid(year,month,day)){  //判断如果日期合法
             if(year%100==a&&month==b&&day==c||        //年月日 、月日年  日月年
                month ==a&&day==b&&year%100==c||
                day ==a &&month ==b&&year%100==c
             )
             printf("%d-%02d-%02d\n",year,month,day);
         }
    }
    return 0;
    
    
}

1238. 日志统计

小明维护着一个程序员论坛。现在他收集了一份”点赞”日志,日志共有 N 行。

其中每一行的格式是:

ts id 表示在 ts 时刻编号 id 的帖子收到一个”赞”。

现在小明想统计有哪些帖子曾经是”热帖”。

如果一个帖子曾在任意一个长度为 D 的时间段内收到不少于 K 个赞,小明就认为这个帖子曾是”热帖”。

具体来说,如果存在某个时刻 T 满足该帖在 [T,T+D) 这段时间内(注意是左闭右开区间)收到不少于 K 个赞,该帖就曾是”热帖”。

给定日志,请你帮助小明统计出所有曾是”热帖”的帖子编号。

输入格式 第一行包含三个整数 N,D,K。

以下 N 行每行一条日志,包含两个整数 ts 和 id。

输出格式 按从小到大的顺序输出热帖 id。

每个 id 占一行。

数据范围 1≤K≤N≤105, 0≤ts,id≤105, 1≤D≤10000

思路: 题目要求得到一段时间范围内的帖子点击数判断是否为热帖,那么我们只需要统计一段时间内的各个帖子的点赞数。那么我们可以遍历每个帖子并且用一个滑动区间来做累加 每次将本次时间的帖子数+1,然后维护区间判断这个区间内当前帖子是否为热帖。存储的话就可以用一个二元组来存储 日期和帖子 并以日期作为第一关键字排序 可以使用pair 默认为第一个关键字排序

#include <bits/stdc++.h>
#include<vector>
using namespace std;
typedef pair<int,int>PII;  //时间和点赞数
const int N = 1e5+10,D=1e4+10;
vector<pair<int,int>>p;
#define x first
#define y second
int n,d,k;
int cnt[N];
bool st[N];
int main(){
    scanf("%d%d%d",&n,&d,&k);
    int t,id;
    for(int i=0;i<n;i++){
        scanf("%d%d",&t,&id);
         p.push_back({t,id});
    }
    sort(p.begin(),p.end());
    
    for(int i=0,j=0;i<n;i++){
        // 需要先将后面的指针数据放进来
        int  ids = p[i].y;  //取出热帖Id
        cnt[ids]++;   //帖子点击量+1 
        while(p[i].x-p[j].x>=d){   //缩进左指针到范围内
           cnt[p[j].y]--;   //左指针帖子点击量-1
           j++;   
        }
        if(cnt[ids]>=k) st[ids]=true;   //如果右指针热帖大于热帖量 那么标记为热帖  
    }
    for(int i=0;i<=N;i++)   //遍历筛选出热帖
     if(st[i])
     cout<<i<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值