• 基础算法(三):双指针/位运算/离散化/区间合并


    目录

    1.双指针算法

    引例

    最长连续不重复子序列

     2.位运算

    n的二进制表示中第k位是几

    lowbit(x)操作:返回x二进制表示中的最后一位1

    3.离散化

    4.区间合并


    1.双指针算法

    引例

    输入一行字符串,输出字符串中的单词,每个单词单独占一行

    1. #include <iostream>
    2. #include <string.h>
    3. using namespace std;
    4. int main()
    5. {
    6. char str[1000];
    7. gets(str);
    8. int n = strlen(str);
    9. for(int i = 0;i<n;i++)
    10. {
    11. int j = i;
    12. while(j<n&&str[j]!=' ')j++;
    13. //这道问题的具体逻辑
    14. for(int k = i;k<j;k++)cout<<str[k];
    15. cout<<endl;
    16. i = j;
    17. }
    18. return 0;
    19. }

    最长连续不重复子序列

     

     check含义检查区间中是否有重复元素,有的话j++。区间中每个数出现的次数可以通过开辟一个数组S[N]来计算,i往后移动一格相当于在数组中加上一个新的数,S[a[i]]++;j往后移动一格相当于[j,i]数组中出去一个数字,S[a[j]]-- 。此处check(j,i)可写成a[j]不等于a[i]

    1. #include<iostream>
    2. using namespace std;
    3. const int N = 100010;
    4. int n;
    5. int a[N],s[N];
    6. int main()
    7. {
    8. cin>>n;
    9. for(int i = 0;i<n;i++)cin>>a[i];
    10. int res = 0;
    11. for(int i = 0,j=0;i<n;i++)
    12. {
    13. s[a[i]]++;
    14. while(s[a[i]]>1)
    15. {
    16. s[a[j]]--;
    17. j++;
    18. }
    19. res = max(res,i-j+1);
    20. }
    21. cout<<res<<endl;
    22. return 0;
    23. }

    当i指针向后移动一个数的时候S[i]=1 

     i再往后移动一格,S[i]=2,执行while

    首先剔除1,[j,i]区间中仍有两个2

    剔除2,j再往后移动一格

     i往后移动一格,此时区间中无重复元素,成立

     

     i再往后移动一格,仍成立

     

     2.位运算

    n的二进制表示中第k位是几

     

    1. #include <iostream>
    2. #include <string.h>
    3. using namespace std;
    4. int main()
    5. {
    6. int n = 10;
    7. for(int k = 3;k>=0;k--)cout<<(n>>k&1);
    8. return 0;
    9. }

    lowbit(x)操作:返回x二进制表示中的最后一位1

    通过x&-x实现。C++中-x的二进制表示和~x+1(x取反加1)表示相同

     

     应用:统计x中1的个数,每一次将x中的最后一位1去掉,当x=0时,减的次数即为1的个数

     

    1. #include <iostream>
    2. using namespace std;
    3. int lowbit(int x)
    4. {
    5. return x & -x;
    6. }
    7. int main()
    8. {
    9. int n;
    10. cin>>n;
    11. while(n--)
    12. {
    13. int x;
    14. cin>>x;
    15. int res = 0;
    16. while(x)x -=lowbit(x),res++;//每次减去x的最后一位1
    17. cout<<res<<' ';
    18. }
    19. return 0;
    20. }

    3.离散化

    1. vector<int> alls; // 存储所有待离散化的值
    2. sort(alls.begin(), alls.end()); // 将所有值排序
    3. alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去掉重复元素
    4. // 二分求出x对应的离散化的值
    5. int find(int x) // 找到第一个大于等于x的位置
    6. {
    7. int l = 0, r = alls.size() - 1;
    8. while (l < r)
    9. {
    10. int mid = l + r >> 1;
    11. if (alls[mid] >= x) r = mid;
    12. else l = mid + 1;
    13. }
    14. return r + 1; // 映射到1, 2, ...n
    15. }

     

    1. #include <iostream>
    2. #include <vector>
    3. #include <algorithm>
    4. using namespace std;
    5. typedef pair<int,int>PII;//每个操作是两位数,用pair来储存
    6. const int N = 300010;
    7. int n,m;
    8. int a[N],s[N];//a[N]存储数字,S[N]前缀和
    9. vector<int> alls;
    10. vector<PII>add,query;//插入操作和求解操作
    11. int find(int x)//find函数用于求x值离散后的结果
    12. {
    13. int l = 0,r = alls.size()-1;
    14. while(l<r)
    15. {
    16. int mid = l+r>>1;
    17. if(alls[mid]>=x)r = mid;//要找>=x的最小的数
    18. else l = mid + 1;
    19. }
    20. return r + 1;//把所有的数映射到从1开始的自然数,因为要用到前缀和,从1开始容易处理边界
    21. }
    22. int main()
    23. {
    24. cin>>n>>m;
    25. for(int i = 0;i<n;i++)
    26. {
    27. int x,c;
    28. cin>>x>>c;
    29. add.push_back({x,c});//在下标x的位置加上c
    30. alls.push_back(x); //把x加到待离散化的数组中
    31. }
    32. for(int i = 0;i<m;i++)
    33. {
    34. int l,r;
    35. cin>>l>>r;//读入所有的左右区间
    36. query.push_back({l,r});//区间的左右端点都是需要离散化的,此处加到query中去
    37. alls.push_back(l);
    38. alls.push_back(r);//把左右区间都加入到待离散化的数组中
    39. }
    40. //去重
    41. sort(alls.begin(),alls.end());//排序
    42. alls.erase(unique(alls.begin(),alls.end()),alls.end());//去掉重复元素
    43. //unique函数:删除重复元素,所有不重复的元素放到数组的前面去,返回新数组的最后一个位置,把新数组的位置到结尾位置中的冗余元素全部删除,剩下的就是不重复的元素
    44. //处理插入
    45. for(auto item : add)
    46. {
    47. int x = find(item.first);
    48. a[x] += item.second;//在离散化之后的坐标的位置上加上要加的数
    49. }
    50. //预处理前缀和
    51. for(int i = 1;i<=alls.size();i++)s[i] = s[i-1]+a[i];
    52. //处理询问
    53. for(auto item : query)
    54. {
    55. int l = find(item.first),r = find(item.second);//左右端点离散化之后的值
    56. cout<<s[r] - s[l-1]<<endl;//中间所有数的个数通过前缀和公式计算
    57. }
    58. return 0;
    59. }

    每一段相同数第一个数满足的性质,unique函数即取出所有满足该性质的数:通过一个双指针算法,将数组中所有不重复的元素(满足下列性质的数)放到前j个位置

    1. vector<int>::iterator unique(vector<int> &a)
    2. {
    3. int j = 0;
    4. for(int i = 0;i<a.size();i++)
    5. if(!i || a[i]!=a[i-1])
    6. a[j++] = a[i];
    7. //a[0]到a[j-1]包含所有a中不重复的数
    8. return a.begin() + j;
    9. }

     使用unique函数

    1. #include <iostream>
    2. #include <vector>
    3. #include <algorithm>
    4. using namespace std;
    5. typedef pair<int,int>PII;
    6. const int N = 300010;
    7. int n,m;
    8. int a[N],s[N];
    9. vector<int> alls;
    10. vector<PII>add,query;
    11. int find(int x)
    12. {
    13. int l = 0,r = alls.size()-1;
    14. while(l<r)
    15. {
    16. int mid = l+r>>1;
    17. if(alls[mid]>=x)r = mid;
    18. else l = mid + 1;
    19. }
    20. return r + 1;
    21. }
    22. vector<int>::iterator unique(vector<int> &a)
    23. {
    24. int j = 0;
    25. for(int i = 0;i<a.size();i++)
    26. if(!i || a[i]!=a[i-1])
    27. a[j++] = a[i];
    28. //a[0]到a[j-1]包含所有a中不重复的数
    29. return a.begin() + j;
    30. }
    31. int main()
    32. {
    33. cin>>n>>m;
    34. for(int i = 0;i<n;i++)
    35. {
    36. int x,c;
    37. cin>>x>>c;
    38. add.push_back({x,c});
    39. alls.push_back(x);
    40. }
    41. for(int i = 0;i<m;i++)
    42. {
    43. int l,r;
    44. cin>>l>>r;
    45. query.push_back({l,r});
    46. alls.push_back(l);
    47. alls.push_back(r);
    48. }
    49. //去重
    50. sort(alls.begin(),alls.end());
    51. alls.erase(unique(alls),alls.end());
    52. //处理插入
    53. for(auto item : add)
    54. {
    55. int x = find(item.first);
    56. a[x] += item.second;
    57. }
    58. //预处理前缀和
    59. for(int i = 1;i<=alls.size();i++)s[i] = s[i-1]+a[i];
    60. //处理询问
    61. for(auto item : query)
    62. {
    63. int l = find(item.first),r = find(item.second);
    64. cout<<s[r] - s[l-1]<<endl;
    65. }
    66. return 0;
    67. }

    4.区间合并

    1. // 将所有存在交集的区间合并
    2. void merge(vector<PII> &segs)
    3. {
    4. vector<PII> res;
    5. sort(segs.begin(), segs.end());
    6. int st = -2e9, ed = -2e9;
    7. for (auto seg : segs)
    8. if (ed < seg.first)
    9. {
    10. if (st != -2e9) res.push_back({st, ed});
    11. st = seg.first, ed = seg.second;
    12. }
    13. else ed = max(ed, seg.second);
    14. if (st != -2e9) res.push_back({st, ed});
    15. segs = res;
    16. }

     

     

     

     

    1. #include<iostream>
    2. #include <algorithm>
    3. #include<vector>
    4. using namespace std;
    5. typedef pair<int,int>PII;
    6. const int N = 100010;
    7. int n;
    8. vector<PII> segs;
    9. void merge(vector<PII> &segs)
    10. {
    11. vector<PII>res;//储存合并后的结果
    12. sort(segs.begin(),segs.end());//对所有区间进行排序
    13. int st = -2e9 ,ed = -2e9; //设置边界值
    14. for(auto seg : segs)
    15. {
    16. if(ed<seg.first)
    17. {
    18. if(st != -2e9)res.push_back({st,ed});
    19. st = seg.first,ed = seg.second;
    20. }
    21. else ed = max(ed,seg.second);//求两个区间的并集,右端点更新成维护的区间的右端点以及当前区间右端点的较大值
    22. }
    23. if(st != -2e9)res.push_back({st,ed});
    24. segs = res;
    25. }
    26. int main()
    27. {
    28. cin>>n;
    29. for(int i=0;i<n;i++)
    30. {
    31. int l,r;
    32. cin>>l>>r;//读入每个区间的左右端点
    33. segs.push_back({l,r});
    34. }
    35. merge(segs);//进行区间合并
    36. cout<<segs.size()<<endl;//返回合并后的序列的长度
    37. return 0;
    38. }

  • 相关阅读:
    希望所有计算机学生能看到这篇c语言教程
    现代图片性能优化及体验优化指南 - 图片资源的容错及可访问性处理
    Linux-RPM与YUM
    mybatis逆向工程的实现
    ConcurrentHashMap 成员、方法分析
    使用uuid做MySQL主键,被老板,爆怼一顿
    整理C++继承、多继承、菱形继承、虚继承
    【规范】代码分支管理规范
    黑马Java热门面试题SSM(六)
    超聚变安装银河麒麟服务器系统ky10-server-x86
  • 原文地址:https://blog.csdn.net/m0_61548909/article/details/125423928