【双指针】

【双指针】

思路

从暴力开始写
然后思考暴力如何优化
一般都是遍历一个找另一个

【模版题】找最长连续不重复子序列

https://ac.nowcoder.com/acm/contest/20960/1014

思路

image

代码

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=200010;
int n,a[N];
map<int,int> q;
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>n;
      for(int i=1;i<=n;i++) cin>>a[i];
      /*
      【思路】
      前面的指针遍历一次
      如果出现重复:后面的指针一直往前跑,直到不重复
      */
      vector<array<int,3>> tmp;
      int ans=-1;
      for(int i=1,j=1;i<=n;i++){
            q[a[i]]++;
            while(q[a[i]]>1){
                  q[a[j]]--;
                  j++;
            }
            tmp.push_back({j,i,i-j+1});
            ans=max(ans,i-j+1);
      }
      vector<PII> an;
      int cnt=0;
      for(auto temp:tmp){
            if(temp[2]==ans){
                  cnt++;
                  an.push_back({temp[0],temp[1]});
            }
      }
      cout<<cnt<<endl;
      sort(an.begin(),an.end());
      for(auto aa:an){
            cout<<aa.first<<" "<<aa.second<<endl;
      }
      return 0;
}

数组元素的目标和

https://www.acwing.com/problem/content/802/

思路

遍历时有三种状态
(1)a[i]+b[j]>x
(2)a[i]+b[j]<x
(3)a[i]+b[j]==x
->i和j一定要都从小开始找嘛?如果都从小开始遍历 更新状态很麻烦
->从小到大遍历一个 另一个从最大开始找->大于就--
->状态始终只有<=

代码

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=100010;
int n,m,x;
int a[N],b[N];
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>n>>m>>x;
      for(int i=1;i<=n;i++) cin>>a[i];
      for(int i=1;i<=m;i++) cin>>b[i];
      //遍历一个找另一个
      //i从小到大 j从大到小:a[i]+b[j]要么<x要么=x
      for(int i=1,j=m;i<=n;i++){
            while(j>=1 && a[i]+b[j]>x) j--;
            if(a[i]+b[j]==x){
                  cout<<i<<" "<<j;
                  break;
            }
      }
      return 0;
}

字符串

https://ac.nowcoder.com/acm/problem/18386
注意存状态方式 只要不爆越简单越暴力越好

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
const int INF=0x3f3f3f3f;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
string s;
//涉及到桶的遍历:数量少不用map直接建桶就行
int a[35];
int cnt=0;
//最坏情况1e6*26也不会爆->直接扫就行
bool is_ok(){
      for(int i=0;i<26;i++){
            if(!a[i]) return false;
      }
      return true;
}
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>s;
      int n=s.size();
      s='.'+s;
      //遍历右端点
      int j=1;
      int ans=INF;
      for(int i=1;i<=n;i++){
            int t=s[i]-'a';
            a[t]++;
            while(is_ok()){
                  ans=min(ans,i-j+1);
                  int tt=s[j]-'a';
                  a[tt]--;
                  j++;
            }
      }
      cout<<ans;
      return 0;
}

丢手绢

https://ac.nowcoder.com/acm/problem/207040
注:我觉得题目中对于a数组的描述有点问题 他没说清楚a[0]是最后一个小朋友到第一个小朋友的距离 还是第一个小朋友到第二个小朋友的距离、、、

思路

image
设计圆圈枚举的题目都可以用取余想
※注意首个点应当从0开始!

代码

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
const int INF=0x3f3f3f3f;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=100010;
int n,a[N];
int pres[N],nxts[N];
signed main(){
      ios::sync_with_stdio(0);
      cin.tie(0);
      cout.tie(0);
      cin>>n;
      int sum=0;
      for(int i=0;i<n;i++){
            cin>>a[i];
            sum+=a[i];
      }
      int j=0;//注意这里 右端点从第一个小朋友(重合的状态)开始
      int cnt=0;
      int ans=-1;
      for(int i=0;i<n;i++){//枚举左端点
            while(cnt<sum/2){//基于贪心的策略:使两个点趋于相对的状态
                  cnt+=a[j];
                  j=(j+1)%n;//圆圈都可以用取余来操作->注意第一个点应当从0开始
            }
            ans=max(ans,min(cnt,sum-cnt));
            cnt-=a[i];//i移动了所以需要减去
      }
      cout<<ans;
      return 0;
}

小柒的幸运数

https://ac.nowcoder.com/acm/contest/111921/C

题目大意

统计各区间逆序对数量 与x最接近的数

思路

一眼看上去是个数论/贡献法/前缀和的题目
->逆序对需要遍历区间->双指针 枚举右端点
若逆序对数量大于x->缩短左端点 否则继续往右枚举
如何统计区间逆序对的数量? 数字只有0-25 直接暴力加减统计

代码

string s;
i64 x;
/*
【滑动窗口思想】统计区间逆序对数量
若逆序对:
大于->缩小尾端
小于->扩大顶端
*/
void solve(){
    cin>>s;
    cin>>x;
    i64 ans=inf_i64;
    int len=s.size();
    s=' '+s;
    int l=1;
    vector<i64> b(26,0);
    i64 res=0;
    for(int i=1;i<=len;i++){
        for(int k=s[i]-'a'+1;k<26;k++){
            res+=b[k];
        }
        i64 cnt=llabs(x-res);
        ans=min_(ans,cnt);
        b[s[i]-'a']+=1LL;
        while(res>x && l<i){
            i64 ress=0;
            for(int k=0;k<s[l]-'a';k++){
                ress+=b[k];
            }
            res-=ress;
            cnt=llabs(x-res);
            ans=min_(ans,cnt);
            b[s[l]-'a']--;
            l++;
        }
    }
    cout<<ans<<endl;
}

Make a Palindrome

https://codeforces.com/contest/2124/problem/D

题目大意

每次选择一个连续区间,从区间中删去第k大的数
可以操作无数次,问能不能将序列变成一个回文序列

思路

(1)只能删去第k大的数
->求出序列中第k大的数cutoff
->三种情况:
a[i]>cutoff 这种数是可以被删去且对序列无影响的->直接默认删去
a[i]==cutoff 这种数可以被删 也可以不删->当备用
a[i]<cutoff 这种数删不掉

(2)序列构成回文数
把所有a[i]<=cutoff拿进数组 双指针判断
头尾要相等
如果不相等:
头尾有可以删的(==cutoff)就删
没有可以删的 因为现在数组里的数也删不掉->无法构成回文

代码

int n,k;
void solve(){
    cin>>n>>k;
	vector<int> a(n+1,0),b(n+1,0);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		b[i]=a[i];
	}
	if(k==1){
		cout<<"YES"<<endl;
		return;
	}
    sort(b.begin()+1,b.end(),cmp);
	int cutoff=b[k];
	vector<int> c;
	
	for(int i=1;i<=n;i++){
		if(a[i]<=cutoff){//注意这里要按照a的原顺序
			c.push_back(a[i]);
		}
	}
	//数组至少要保留k-1个元素
	int spare=c.size()-(k-1);//能删的次数
	int L=0,R=c.size()-1;
	while(L<R){
		if(c[L]!=c[R]){
			if(spare==0){
				cout<<"NO"<<endl;
				return;
			}
			if(c[L]==cutoff){
				L++;
				spare--;
			}
			else if(c[R]==cutoff){
				R--;
				spare--;
			}
			else{
				//即使中间有cutoff可以删->首尾删不了 那么回文永远无法成立
				cout<<"NO"<<endl;
				return;
			}
			continue;
		}
		L++;
		R--;
	}
	cout<<"YES"<<endl;
}
posted @ 2025-01-24 15:05  White_ink  阅读(5)  评论(0)    收藏  举报