每日四题打卡-3.19:最长不连续子串/数组元素和/二进制1的个数/离散化

1、最长不连续子串

给定一个长度为n的整数序列,请找出最长的不包含重复数字的连续区间,输出它的长度。

输入格式

第一行包含整数n。

第二行包含n个整数(均在0~100000范围内),表示整数序列。

输出格式

共一行,包含一个整数,表示最长的不包含重复数字的连续子序列的长度。

数据范围

1≤n≤1000001≤n≤100000

输入样例:

5
1 2 2 3 5

输出样例:

3
//步骤:定义i,j两指针s[]数组存当前j到i区间每一个数出现的次数,s[a[i]] ++;//标记第a[i]个位置的数,表示加入了这个数,
//如果出现重复 s[a[j]] --;//这里弄错了,注意这里是a[j],把j拿出去;j ++;//把j往后移
//res = max(res, i - j + 1);//存最大的数
#include <iostream>

using namespace std;
const int N = 100010;

int a[N], s[N];

int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i ++) cin >> a[i];
    int res = 0;//存最长连续不重复子序列
    for (int i = 0, j = 0; i < n; i ++){
        s[a[i]] ++;//标记第a[i]个位置的数,表示加入了这个数
        while (s[a[i]] > 1)//判断a[i]位置的数是否出现重复,如果<=1说明没有重复
        {
            //如果出现重复
            s[a[j]] --;//这里弄错了,注意这里是a[j],把j拿出去
            j ++;//把j往后移
        }
        res = max(res, i - j + 1);//存最大的数
    }
    cout << res << endl;
    return 0;
}

2、数组元素目标和

给定两个升序排序的有序数组A和B,以及一个目标值x。数组下标从0开始。
请你求出满足A[i] + B[j] = x的数对(i, j)。

数据保证有唯一解。

输入格式

第一行包含三个整数n,m,x,分别表示A的长度,B的长度以及目标值x。

第二行包含n个整数,表示数组A。

第三行包含m个整数,表示数组B。

输出格式

共一行,包含两个整数 i 和 j。

数据范围

数组长度不超过100000。
同一数组内元素各不相同。
1≤数组元素≤1091≤数组元素≤109

输入样例:

4 5 6
1 2 4 7
3 4 6 8 9

输出样例:

1 1
//步骤:根据单调性来解决这个问题,首先a从0开始,b从最后一位m-1开始,满足j >= 0 && a[i] + b[j] > x j --往后移
//当不满足上述条件跳出循环然后输出满足条件的i,j的值

#include <iostream>

using namespace std;

const int N = 100010;

int a[N], b[N];

int main()
{
    int n, m, x;
    cin >> n >> m >> x;
    for (int i = 0; i < n; i ++) cin >> a[i];
    for (int j = 0; j < m; j ++) cin >> b[j];
    //暴力解法:超时了
    // for (int i = 0; i < n; i ++)
    //     for (int j = 0; j < m; j ++)
    //         if (a[i] + b[j] == x) cout << i << ' '<< j <<endl;
            
    //优化:注意两个升序排序的有序数组A和B,所以我们使用使用单调性来解决这个问题
    
    for (int i = 0, j = m - 1; i < n; i ++){
        while (j >= 0 && a[i] + b[j] > x) j --;//a[i]和b[i]进行相加,如果大于x,则B数组末尾数j往前移动一位,直到A + B <= x
        if (a[i] + b[j] == x)//满足条件输出i, j的值。
        {
            printf("%d %d\n", i, j);
            break;
        }
    }
    return 0;
}

3、二进制中1的个数

给定一个长度为n的数列,请你求出数列中每个数的二进制表示中1的个数。

输入格式

第一行包含整数n。

第二行包含n个整数,表示整个数列。

输出格式

共一行,包含n个整数,其中的第 i 个数表示数列中的第 i 个数的二进制表示中1的个数。

数据范围

1≤n≤1000001≤n≤100000,
0≤数列中元素的值≤1090≤数列中元素的值≤109

输入样例:

5
1 2 3 4 5

输出样例:

1 1 2 1 2
/*
输出一个数的二进制
例:n = 15 = (1111)2  1、先把第k位移到最后一位 n >> k。2、看个位是几 x&13、第1,2步部合起来:n >> k & 1.
lowbit(x)作用:返回x的最后一位1
例:x = 1010  lowbit(x) = 10
x = 1010000  lowbit(x) = 10000
公式:lowbit(x) = x & -x;
概念:在计算机中补码 -x = ~x(反码) + 1 ---> x & -x = x & (~x + 1)
例:x = 1010 .... 100 ... 0
~x = 0101.... 01 ... 1
~x + 1 = 0101 ... 100 ,,, 0
x & (~x + 1) = 0 ... 010 .... 0
*/
#include <iostream>
using namespace std;
const int N = 100010;
int a[N];
int lowbit (int x){
    return x & -x;
}
int main()
{
    int n;
    cin >> n;
    //输入形式搞错了,得注意
    while (n --){
        int x;
        cin >> x;
        
        int res = 0;
        while(x) x -= lowbit(x), res ++;//忘了,一定要记住核心步骤:每次减去x的最后一位1,然后res++标记
        cout << res << " ";
    }
}

4、离散化-区间和

假定有一个无限长的数轴,数轴上每个坐标上的数都是0。

现在,我们首先进行 n 次操作,每次操作将某一位置x上的数加c。

接下来,进行 m 次询问,每个询问包含两个整数l和r,你需要求出在区间[l, r]之间的所有数的和。

输入格式

第一行包含两个整数n和m。

接下来 n 行,每行包含两个整数x和c。

再接下里 m 行,每行包含两个整数l和r。

输出格式

共m行,每行输出一个询问中所求的区间内数字和。

数据范围

−109≤x≤109−109≤x≤109,
1≤n,m≤1051≤n,m≤105,
−109≤l≤r≤109−109≤l≤r≤109,
−10000≤c≤10000−10000≤c≤10000

输入样例:

3 3
1 2
3 6
7 5
1 3
4 6
7 8

输出样例:

8
0
5
/*
离散化:如果一个数值范围是0-10^9,数值域特别大,个数比较小,比如只有10^5个数(值域跨度很大,数分布很稀疏)
如果开10^9区域特别浪费内存。所以我们需要把他们映射到从0开始的连续的自然数。
例:数组a[] = 1, 3, 100, 2000, 500000.数值很大,但是里面的数很小。我们使用0,1,2,3,4,来分别映射到1,3,100,2000,500000中.这个过程就叫做离散化。
离散化存在问题:1、a[]中可能有重复元素,我们需要去重。2、如何算出x的值在a[]中的下标是什么?即离散化之后的值。
步骤:1、存储所有待离散化的值。2、将所有值排序。3、去掉重复元素。4、二分求出离散化的值。
*/
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

typedef pair<int, int> PII;//使用paire来存操作
const int N = 300010;//数据范围n + 2m 所以三十万
int a[N], s[N];//a是存的数,s是前缀和
int n, m;

vector<int> alls;//存所有离散化的值
vector<PII> add, query;//先通过操作读出来,add-插入操作,query-求

//求x值离散化之后的结果,映射到从1开始的前缀和
int find(int x)
{
    int l = 0, r = alls.size() - 1;
    while(l < r)
    {
        int mid = l + r >> 1;
        if(alls[mid] >= x) r = mid;//二分法
        else l = mid + 1;
    }
    return r + 1;
}

//unique函数实现 返回vector迭代器。实现原理:双指针算法
//不重复数要求:1、他是第一个数 2、或者a[i] != a[i - 1]
vector<int>:: iterator unique(vector<int> &a)
{
    int j = 0;
    for(int i = 0; i < a.size(); i ++)//i遍历所有数,j存到了第几个数。j <= i
        if(!i || a[i] != a[i - 1])
            a[j ++ ] = a[i];
    return a.begin() + j;
    //得到a[0] ~ a[j - 1]范围的数不重复
}

int main()
{

    cin >> n >> m;

    for(int i = 0; i < n; i ++ )//插入操作
    {
        int x, c;
        cin >> x >> c;
        add.push_back({x, c});//插入

        alls.push_back(x);//加入到离散化数组
    }

    for(int i = 0; i < m; i ++ )//读入所有的左右区间
    {
        int l, r;
        cin >> l >> r;
        query.push_back({l, r});//加入到模块
        //将左右区间加入到离散化alls数组
        alls.push_back(l);
        alls.push_back(r);
    }

    sort(alls.begin(), alls.end());//1、排序
    alls.erase(unique(alls), alls.end());//2、将里面重复元素去掉

    for(auto item : add)//插入操作
    {
        int x = find(item.first);//求x离散化结果
        a[x] += item.second;//离散化之后的坐标上加上要加的数
    }

    //预处理前缀和
    for(int i = 1; i <= alls.size(); i ++ ) s[i] = s[i - 1] + a[i];
    
    //处理询问
    for(auto item : query)
    {
        int l = find(item.first), r = find(item.second);//l是左区间,r是右区间
        cout << s[r] - s[l - 1] << endl;
    }

    return 0;
}
发布了176 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_27262727/article/details/104960756