2023寒假训练week3

Day1

2023牛客寒假算法基础集训营4

A.清楚姐姐学信息论

1.看懂题目,看出规律
2.求导

#include<iostream>
using namespace std;
int main()
{
    long long x,y;
    cin>>x>>y;
    if(x==3||y==3)cout<<3;  // 3的时候效率最大
    else cout<<min(x,y);
}

L.清楚姐姐的三角形I

给定三个数,两两相加已知。判断他们能否组成三角形

#include<bits/stdc++.h>
using namespace std;
signed main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        int sum = (a+b+c)/2;
        int la = ((b+c) - a)/2; //a的长度
        int lb = ((a+c) - b)/2;
        int lc = ((a+b) - c)/2;
        if (la <= 0 || lb <= 0 || lc <= 0 || ((b+c) - a) % 2!=0) {//不能是奇数,每条边不能小于0
            cout << "No"<<endl;
        }else if(la + lb <= lc || la + lc <= lb || lb + lc <= la){//两边之和大于第三边
            cout<<"No"<<endl;
        }else if(abs(la - lb) >= lc || abs(la - lc) >= lb || abs(lb - lc) >= la){//三角形两边之差小于第三边
            cout<<"No"<<endl;
        }else{
            cout<<"Yes"<<"\n"<<la<<" "<<lb<<" "<<lc<<endl;
        }
        
    }
}

M.清楚姐姐的三角形II

1.相邻三项一定不能组成三角形
2.只要满足两边之和等于第三边就可以
循环输出112显然不错

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,i;
    cin>>n;
    long long a[n];
    a[0]=1;
    a[1]=1;
    a[2]=2;
    for(i=0;i<n;i++)
    {
        a[i]=a[i%3];
        cout<<a[i]<<" ";
    }
    return 0;
}

每日一题【模板】并查集

完成合并和查询操作

#include<bits/stdc++.h>
using namespace std;
const int N=1e5; 
int n,m,z;
int fa[N];

int find_(int x)//返回x的祖宗结点 
{
	if(fa[x]!=x) fa[x]=find_(fa[x]);//如果x不等于祖宗结点
	return fa[x];//让他找到x的根节点,并让x等于他的根结点 
}

void union_(int x,int y)
{
	fa[find_(x)]=find_(y);//让x的祖宗结点等于y的祖宗结点 
}

int main()
{
	cin>>n>>m;
	//初始化 
	for(int i=0;i<n;i++) fa[i]=i;
	int x,y;
	for(int i=0;i<m;i++)
	{
		cin>>z;
		cin>>x>>y;
		if(z==1){
			union_(x,y);
		} 
		else{
			if(find_(x)==find_(y)){
				cout<<"Y"<<endl;
			}
			else{
				cout<<"N"<<endl;
			}
		}
	}
	return 0;
 } 

Day2

洛谷P1551 亲戚(并查集)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5; 
int n,m,p;
int fa[N];

int find(int x)//返回x的祖宗结点 
{
	if(fa[x]!=x) fa[x]=find(fa[x]);//如果x不等于祖宗结点
	return fa[x];//让他找到x的根节点,并让x等于他的根结点 
}

void union_(int x,int y)
{
	fa[find(x)]=find(y);//让x的祖宗结点等于y的祖宗结点 
}

int main()
{
	cin>>n>>m>>p;
	for(int i=0;i<n;i++) fa[i]=i;
	while(m--)
	{
		int m1,m2;
		cin>>m1>>m2;
		union_(m1,m2);
	}
	while(p--)
	{
		int p1,p2;
		cin>>p1>>p2;
		if(find(p1)==find(p2))
		{
			cout<<"Yes"<<endl;
		}
		else
		{
			cout<<"No"<<endl;
		}
	}
	return 0;
}

洛谷P1196 [NOI2002] 银河英雄传说

ACwing数组元素的目标和(双指针)

找出两个升序数组,满足a[i]+b[j]=x的数对(i, j)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+100;
LL 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 i=0;i<m;i++) cin>>b[i];
    //均升序
    for(int i=0,j=m-1;i<n;i++){
        while(j>=0&&a[i]+b[j]>x) j--;
        if(a[i]+b[j]==x){
            cout<<i<<" "<<j<<endl;
            break;
        }
    }
    return 0;
}

ACwing判断子序列(双指针)

请你判断a序列是否为b序列的子序列

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N];

int main()
{
    int n,m;
    cin>>n>>m;
    
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<m;i++) cin>>b[i];
    
    int i=0,j=0;
    while(i<n&&j<m){
        if(a[i]==b[j]) i++; //匹配到一组,i++到下一个数
        j++;    //不管有没有匹配,j都要到下一个数
    }
    if(i==n) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
    
    return 0;
}

ACwing最长连续不重复子序列(双指针)

一段长度为n的序列,要求找出最长的不重复子序列

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int 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]>1就说明a[i]不只出现1次,有重复
        while(s[a[i]]>1)
        {
            s[a[j]]--;
            j++;    //j从左开始,依次弹出a[j],直到重复元素a[i]。
        }
        res=max(res,i-j+1);
    }
    cout<<res<<endl;
    return 0;
}

ACwing区间和(离散化、前缀和)

1.离散化存储每个数据
2.前缀和求区间和

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

const int N = 300010; //n次插入和m次查询相关数据量的上界
int n, m;
int a[N];//存储坐标插入的值
int s[N];//存储数组a的前缀和
vector<int> alls;  //存储(所有与插入和查询有关的)坐标
vector<pair<int, int>> add, query; //存储插入和询问操作的数据

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 l;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        int x, c;
        scanf("%d%d", &x, &c);
        add.push_back({x, c});
        alls.push_back(x);
    }
    for (int i = 1; i <= m; i++) {
        int l , r;
        scanf("%d%d", &l, &r);
        query.push_back({l, r});
        alls.push_back(l);
        alls.push_back(r);
    }
   //排序,去重
    sort(alls.begin(), alls.end());
    alls.erase(unique(alls.begin(), alls.end()), alls.end());
    //执行前n次插入操作
    for (auto item : add) {
        int x = find(item.first);
        a[x] += item.second;
    }
    //前缀和
    for (int i = 1; i <= alls.size(); i++) s[i] = s[i-1] + a[i];
    //处理后m次询问操作
    for (auto item : query) {
        int l = find(item.first);
        int r = find(item.second);
        printf("%d\n", s[r] - s[l-1]);
    }

    return 0;
}

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

  1. 将n右移k位(将n的第k位移到最后一位)
    n>>k ;
  2. n的最后一位是几
    n & 1 ;

10的二进制表示

#include<bits/stdc++.h>
using namespace std;
int main()
{
       int n=10;
       for(int k=3;k>=0;k--) cout<<(n<<k)&1;
       return 0;
}

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

#include<bits/stdc++.h>
using namespace std;
int main()
{ 
      int n;
      cin>>n;
      int k;
      cin>>K;
      cout<<(n<<k)&1;
}

ACwing二进制中1的个数

lowbit(x)操作返回x的最后一位1

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long LL;

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);   //x-x的最后一位1直到把1都减无
            res++;
        }
        cout<<res<<" ";
    }
    return 0;
}

ACwing区间合并

1.要求合并所有有交集的区间

2.输出合并完成后的区间个数

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef pair<int,int>PII;
vector<PII>segs;
int n;

void merge(vector<PII> &segs){
    vector<PII> res;
    // 左端点排序
    sort(segs.begin(), segs.end());
    // 左右端点初始化,-无穷
    int start = -2e9, end = -2e9;
    for(auto seg: segs){
        if(end < seg.first){
            // 初始的[-无穷,-无穷]区间要跳过,不能装入
            if(start != -2e9) res.push_back({start, end});
            start = seg.first, end = seg.second;
        }
        else end = max(end, seg.second);
    }
    // 有两个作用,1.是防止n为0,把[-无穷,-无穷]压入;2.是压入最后一个(也就是当前)的区间,若n>=1,if可以不要
    if (start != -2e9) res.push_back({start, end});
    //覆盖segs
    segs = res;
}

int main()
{
    cin>>n;
    for(int i=0;i<n;i++){
        int l,r;
        cin>>l>>r;
        segs.push_back({l,r});
    }
    merge(segs);
    cout<<segs.size()<<endl;
    return 0;
}

ACwing滑动窗口(单调队列)
1.
确定滑动窗口位于每个位置时,窗口中的最大值和最小值。

第一行输出,从左至右,每个位置滑动窗口中的最小值。

第二行输出,从左至右,每个位置滑动窗口中的最大值。

2.因为窗口位三个数,后面进来的数一定比前面进来的数后出。因此以最大值为例,若后一个

入队的数比前一个入队的数大,则前面的数永远没有出头之日,要被弹出队列。
双端队列版本:

双端队列相当于加强版vector,支持前后端弹出缺点就是比较慢

使用它主要是因为有clear()操作

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];

int main()
{
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];//读入数据
    deque<int> q;
    for(int i = 1; i <= n; i++)
    {
        while(!q.empty()&& q.back() > a[i]) //新进入窗口的值小于队尾元素,则队尾出队列
            q.pop_back();
        q.push_back(a[i]);//将新进入的元素入队
        if(i - k >= 1 && q.front() == a[i - k])//若队头是否滑出了窗口,队头出队 
            q.pop_front();
        if(i >= k)//当窗口形成,输出队头对应的值
            cout << q.front() <<" ";
    }
    q.clear();
    cout << endl;

    //最大值亦然
    for(int i = 1; i <= n; i++)
    {
        while(!q.empty()&& q.back() < a[i]) q.pop_back();
        q.push_back(a[i]);
        if(i - k >= 1 && a[i - k] == q.front()) q.pop_front(); 
        if(i >= k) cout << q.front() << " ";

    }
}

Day3

SMU Winter 2023 Round #9 (Div.2)

A. Who is The 19th ZUCCPC Champion

随便输出一个字符串就行。

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

int main(){
    cout<<"Ranni the Witch\n";
    return 0;
}

B. Jiubei and Overwatch

  1. t<=k,damage=tx;
  2. t>k,damage=kx+(t-k)y;
    当时间小于k的时候,就是t倍的x。超过k的话,超过多少就加多少倍的y。
    排序找出最大的防御力的怪物,因为同时攻击,最大的灭了,其他小的自然就灭了。
    分成两部分求解:
  3. 没超过k
  4. 超过k
#include<bits/stdc++.h>
using namespace std;
const int N=101;
int n,k,x,y;
int a[N];

void sovle()
{
	cin>>n>>k>>x>>y;
	for(int i=0;i<n;i++) cin>>a[i];
	sort(a,a+n,greater<int>());
//	cout<<a[0]<<endl;
	for(int t=1;;t++){
		if(t<=k)
		{
			int dam=t*x;
			if(a[0]<=dam)
			{
				cout<<t<<endl;
				break;
			} 
		}
		else{
			int dam=k*x+(t-k)*y;
			if(a[0]<=dam)
			{
				cout<<t<<endl;
				break;
			}
		}
	} 
	
}


int main()
{
	int _;
	cin>>_;
	while(_--){
		sovle();
	}
}

C. Ah, It's Yesterday Once More

交换排序:

for(i->n)
  for(j->n)    
    if(ai<aj) swap(ai,aj);

冒泡排序:

for(i->n)
  for(j->n-1)
    if(aj>a(j+1))  swap(ai,a(j+1));

题目要求找出全排列数组a,使得冒泡排序和交换排序的交换次数相同。
显然倒序输出的时候相同。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;

void sovle()
{
	int n;
	cin>>n;
	for(int i=n;i>0;i--)
	{
		cout<<i<<" ";
	}
	cout<<endl;
}


int main()
{
	int _;
	cin>>_;
	while(_--){
		sovle();
	}
}

F. Sum of Numerators

将分数化简到最小的分子相加。
1.奇数时:分子直接相加
2.偶数时:分子不断除2
3.找规律

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
ll t;
ll n,k;
int main(){
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld",&n,&k);
		ll sum=n*(n+1)/2;  //等差数列前n项和,未经处理的分子的和。
		
		while(n&&k){ 
			n/=2;  //会被约分的数字个数每次都是总长的一半
			k--;  //每次被约分,2的次数减一
			sum-=n*(n+1)/2;  //每次被约分掉的总和是等差数列求1到被约分掉的数字个数之和。
		}
		printf("%lld\n",sum);
	}
	return 0;
}

L. Monster Tower

塔高为n,每层都有一个怪物,怪物能量为ai,找出初始的攻击力x最小可以为多少。
1.每次只能在1~k层攻击怪物,每次消灭掉怪物i之后,自身的攻击力将会提升ai
2.消灭掉这一层怪物后,这一层会消失,这层往上的层数会逐层-1
思路:
打怪的顺序是唯一的,所以维护一个有 k 个元素的小根堆,每次处理最小的元素,如果现有能力值大于怪的能力值,加上怪的能力值,否则更新初始值;

每遇到一个打不过的怪,最小可能初始值 = 怪的能力值-前面获得的能力值总和,用这个值去更新之前的 初始值,取其中大的;

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+100;

ll t,n,k;

ll a[N];
priority_queue<ll,vector<int>,greater<int> >pmin;

ll sta,now;

int main()
{
	cin>>t;
	
	while(t--)
	{
		cin>>n>>k;
		
		sta=0;now=0;
		
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);
		}
		
		for(int i=1;i<=n;i++)//每个堆里只有k个元素
		{
			if(pmin.empty()||pmin.size()<k) pmin.push(a[i]);//注意判空
			else
			{
				ll s=pmin.top();
				pmin.pop();
				
				if(now>=s)
				{
					now+=s;
				}//总能力值大于怪的能力值
				else
				{
					sta=max(sta,s-now);
					now+=s;
				}//更新初始值,注意更新方式
				
				pmin.push(a[i]);
			}
		}
		
		
		while(!pmin.empty())//清空堆
		{
			ll s=pmin.top();
			pmin.pop();
				
			if(now>=s)
			{
				now+=s;
			}
			else
			{
				sta=max(sta,s-now);
				now+=s;
			}
		}
		
				
		cout<<sta<<endl;		
		
	}
		
	
	return 0;
}

Day4

洛谷P1111 修复公路(并查集)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6; 
int fa[N],num,ans;

struct node{
	int x,y,t;	
	}e[N];
	
bool operator<(node a,node b){//重载小于运算符
	return a.t<b.t;
	}
	
int find(int x)
{
	if(fa[x]!=x)
	{
		fa[x]=find(fa[x]);
	}
	return fa[x];
}

void union_(int x,int y,int t)
{
	if(find(x)==find(y));
	else{
		fa[find(x)]=find(y);
		num++;
		ans=max(ans,t);
	}
}

int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++)
	{

		cin>>e[i].x>>e[i].y>>e[i].t; 
	}
	sort(e+1,e+m+1);//按照时间排序
	
	for(int i=1;i<=m;i++) union_(e[i].x,e[i].y,e[i].t);//按时间大小合并 
	
	if(num!=n-1) cout<<-1<<endl;
	else cout<<ans<<endl;
	return 0;
}

leetcode242.有效字母异位词(数组哈希)

class Solution {
public:
    bool isAnagram(string s, string t) {
        if(s.size()!=t.size())return 0;

        int res[26];
        for(int i=0;i<26;i++) res[i]=0;

        for(int i=0;i<s.size();i++){
            res[s[i]-'a']++;
        }
        for(int i=0;i<t.size();i++){
            res[t[i]-'a']--;
        }
        for(int i=0;i<26;i++){
            if(res[i]){
                return 0;
            }
        }
        return 1;
    }
};

leetcode349.两数组的交集(set哈希)

1.用unodered_set模拟hash处理

2.用数组模拟hash处理

法1:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int>result;//存放交集
        unordered_set<int> num_set(nums1.begin(),nums1.end());//直接将num1转化为unoderde_set类型

        for(int num : nums2)//看num2里的元素是否在num1中出现
        {
            if(num_set.find(num)!=num_set.end())//如果找不到对应元素,返回的是末尾的迭代器。
            {
                result.insert(num);
            }
        }
        return vector<int>(result.begin(),result.end());
    }
};

法2:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int>result;//存放交集
        int hash[1005]={0};//hash数组,初始化为0
        for(int num : nums1)
        {
            hash[num]=1;
        }
        for(int num : nums2)
        {
            if(hash[num]){
                result.insert(num);//nums2的元素出现过,直接插入result中
            }
        }
        return vector<int>(result.begin(),result.end());
    }
};

leetcode1.两数之和(map哈希)

找出和为目标值target的那两个整数,并返回它们的数组下标。
num1+num2=target
1.key值存放num2,value存放下标。

2.遍历num1,看看map中是否存储了num2,防止重复计算。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map <int,int> map;//无序,first存储数值,second存储下标
        
        for(int i=0;i<nums.size();i++){//查询map中是否有相加等于target的元素
            
            auto it=map.find(target-nums[i]);
            if(it!=map.end())//map中,找得到和为target的另一个数的下标
               return {it->second,i};
            
            // 如果没找到匹配对,就把访问过的元素和下标加入到map中
            map.insert(pair<int,int>{nums[i],i});
        }
        return {};
    }
};

leetcode454.四数相加(II)(map哈希)

给定四个数组,求num1[i]+num2[j]+num3[k]+num4[l]=target,一共有几种方法

1.朴素写法

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int>map;

        for(int i=0;i<nums1.size();i++){//将nums1+nums2的值存在map中
            for(int j=0;j<nums2.size();j++){
                map[nums1[i]+nums2[j]]++;
            }
        }
        int conut=0;
        for(int i=0;i<nums3.size();i++){
            for(int j=0;j<nums4.size();j++){
                auto it=-(nums3[i]+nums4[j]);
                if(map.find(it)!=map.end()){//找到了
                        conut+=map[it];
                }
            }
        }
        return conut;
    }
};

2.优化

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int>map;

        for(int i : nums1){//将nums1+nums2的值存在map中
            for(int j : nums2){
                map[i+j]++;
            }
        }
        int conut=0;
        for(int i : nums3){
            for(int j : nums4){
                auto it=-(i+j);
                if(map.find(it)!=map.end()){//找到了
                        conut+=map[it];
                }
            }
        }
        return conut;
    }
};

leetcode15.三数之和(双指针)

1.给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c 。使得 a

+b+c=0请你找出所有满足条件且不重复的三元组。

2.答案中不可以包含重复的三元组。

详细介绍:
例如答案为:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
这题的麻烦主要时去重

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        
        sort(nums.begin(),nums.end());//排序
        vector<vector<int>> result;

        for(int i=0;i<nums.size();i++){          
            if(nums[i]>0) return result;//最小的数大于0,说明不管怎相加都不会等于0

            if(i>0&&nums[i]==nums[i-1])//去重复
                continue;

            int l=i+1;
            int r=nums.size()-1;
            while(l<r){
                if(nums[i]+nums[l]+nums[r]>0){
                    r--;
                }
                else if(nums[i]+nums[l]+nums[r]<0){
                    l++;
                }
                else{
                    result.push_back(vector<int>{nums[i] ,nums[l] ,nums[r]});
                    //接下来要对l和r去重
                    while(l<r&&nums[l]==nums[l+1]) l++;
                    while(l<r&&nums[r]==nums[r-1]) r--;
                    //找到result后,将l和r收缩
                    l++;
                    r--;
                }
            }
        }
    return result;
    }
};

leetcode18.四数之和(双指针)

1.剪枝
2.去重
3.三数之和

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(),nums.end());

        for(int a=0;a<nums.size();a++){
            if(nums[a]>0&&target>0&&nums[a]>target){//剪枝
                break;
            }
            if(a>0&&nums[a]==nums[a-1]){//对a去重
                continue;
            }
            for(int b=a+1;b<nums.size();b++){//剪枝
                if(nums[b]+nums[a]>0&&target>0&&nums[a]+nums[b]>target){
                    break;
                }
                if(b>a+1&&nums[b]==nums[b-1]){//对b去重
                    continue;
                }
                
                int c=b+1,d=nums.size()-1;
                while(c<d){
                     // nums[a] + nums[b] + nums[c] + nums[d] > target 会溢出
                    if((long)nums[a]+nums[b]+nums[c]+nums[d]>target) d--;
                    else if((long)nums[a]+nums[b]+nums[c]+nums[d]<target) c++;
                    else{
                        result.push_back(vector<int>({nums[a],nums[b],nums[c],nums[d]}));
                        //获得答案之后对c和d进行去重
                        while(c<d&&nums[c]==nums[c+1]) c++;
                        while(c<d&&nums[d]==nums[d-1]) d--;
                        //获得答案后将范围收缩
                        c++;
                        d--;
                    }
                }
            }
        }
        return result;
    }
};

leetcode344.反转字符串(I)(双指针)

要求空间复杂度o(1)即不能另外开一个字符串,而改变s字符串的值

class Solution {
public:
    void reverseString(vector<char>& s) {
        char t;
        for(int i=0,j=s.size()-1;i<j;i++,j--){
            t=s[j];
            s[j]=s[i];
            s[i]=t;
        }
    }
};

leetcode541.反转字符串(II)(双指针)

给定一个字符串 s 和一个整数 k,从字符串开头算起, 每计数至 2k 个字符,就反转这 2k 个字符中的前 k 个字符。

如果剩余字符少于 k 个,则将剩余字符全部反转。

如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

示例:

输入: s = "abcdefg", k = 2
输出: "bacdfeg"

1.可以自己写一个反转函数

class Solution {
public:
    string reverseStr(string s, int k) {
        for(int i=0;i<s.size();i+=(2*k)){ // 以2k间距来操作
            if(i+k<=s.size()){ //i后面字符大于k个,反转k个字符
                for(int j=i,l=i+k-1;j<l;j++,l--){//注意在[i,i+k)区间内反转
                    char t=s[j];
                    s[j]=s[l];
                    s[l]=t;
                }
                continue;
            }
            else{ //到最后,i后面的字符小于k个
                for(int j=i,l=s.size()-1;j<l;j++,l--){
                    char t=s[j];
                    s[j]=s[l];
                    s[l]=t;
                }
            }
        }
        return s;
    }
};

2.可以用stl函数

class Solution {
public:
    string reverseStr(string s, int k) {
        for(int i=0;i<s.size();i+=(2*k)){ // 以2k间距来操作
            if(i+k<=s.size()){ //i后面字符大于k个,反转k个字符
                reverse(s.begin() + i, s.begin() + i + k );
                continue;
            }
            else{ //到最后,i后面的字符小于k个
                 reverse(s.begin() + i, s.end());
            }
        }
        return s;
    }
};

Day6

洛谷P1536 村村通(并查集)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5; 
int n,m;
int fa[N];

int find(int x)//返回x的祖宗结点 
{
	if(fa[x]!=x) fa[x]=find(fa[x]);//如果x不等于祖宗结点
	return fa[x];//让他找到x的根节点,并让x等于他的根结点 
}

void union_(int x,int y)
{
	fa[find(x)]=find(y);//让x的祖宗结点等于y的祖宗结点 
}

int main()
{
	while(1)
	{
		cin>>n;
		if(n==0) return 0;
		
		cin>>m;
		for(int i=1;i<=n;i++) fa[i]=i;//城镇 
		
		while(m--)
		{
			int m1,m2;
			cin>>m1>>m2;//由这条道路相连的城镇编号 
			union_(m1,m2);
		}
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			if(find(i)==i)
			{
				ans++;
			}
		}
		cout<<ans-1<<endl;
	} 
	return 0;
}

leetcode27. 移除元素(双指针)

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slow=0;
        for(int fast=0;fast<nums.size();fast++){
            if(nums[fast]!=val){ //快指针的值不等于val时将元素的值赋给nums[slow]
                nums[slow]=nums[fast];
                slow++; //慢指针移动
            }
        }
        return slow;
    }
};

leetcode151. 反转字符串中的单词(双指针)

给定一个字符串,逐个翻转字符串中的每个单词。

示例 1:
输入: "the sky is blue"
输出: "blue is sky the"

示例 2:
输入: " hello world! "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。

示例 3:
输入: "a good example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
一定要记得resize操作

class Solution {
public:
    string reverseWords(string s) {
        //空格移除操作
        int slow=0;//慢指针
        for(int fast=0;fast<s.size();fast++){//快指针
            if(s[fast]!=' ')//如果fast指向的元素不为空格
            {
                if(slow!=0)//如果slow指向的元素不为首元
                {
                    s[slow]=' ';//就在单词前加空格
                    slow++;
                }
                while(fast<s.size()&&s[fast]!=' ')//当在数组内,且不等于空格时
                {
                    s[slow]=s[fast];//覆盖
                    slow++;
                    fast++;
                }
            }
        }
        s.resize(slow); //slow的大小即为去除多余空格后的大小。

        //反转操作,先反转整个句子,再逐个反转单词
        for(int i=0,j=s.size()-1;i<j;i++,j--)
        {
            char t=s[i];
            s[i]=s[j];
            s[j]=t;
        }

        int st=0;//起始的单词字母在0处
        for(int i=0;i<=s.size();i++){
            if(s[i]==' '||i==s.size()){  //到达单词结尾
                for(int l=st,r=i-1;l<r;l++,r--){
                    char t=s[l];
                    s[l]=s[r];
                    s[r]=t;
                }
                st=i+1; //新单词的起始位置

            }
        }
        return s;
    }
};
posted @ 2023-02-05 14:58  Is_Qinna  阅读(43)  评论(0)    收藏  举报