牛客寒假训练4--A.R

12 篇文章 0 订阅

题目传送门:A.R
题目描述:
小红拿到了一个长度为 n 的字符串,该字符串仅由大写字母组成。
小红很喜欢红色(用’R’字母表示),但她非常讨厌紫色(用’P’字母表示)。
她想取一个连续子串,该子串包含至少 k 个’R’字符,且不能包含’P’字符。
你能告诉她有多少合法的方案可以取到吗?
注:只要连续子串的起始位置或终止位置不同,我们就认为是两个不同的方案。
输入描述:
第一行输入两个正整数 n 和 k ,用空格隔开。
输入一行字符串,该字符串保证仅包含大写字母(‘A’到’Z’)。
数据范围:
1≤n≤200000
1≤k≤20
输出描述:
取一个连续子串,包含至少 k 个’R’字符、且不包含’P’字符的方案数。
题目大意:
在一串字符串中取连续子串,要求子串必须包含有至少k个’R’字符且不能包含’P’字符,求方案数。
题解一: 二分法
时间复杂度: O(nlogn)
思路:
题目具有两个条件,一是包含至少 k 个’R’字符,二是不包含’P’字符。
由此可见,总共有四种情况:
1.不足k个‘R’字符且不包含‘P’字符。
2.不足k个‘R’字符且包含‘P’字符。
3.足够k个‘R’字符且不包含‘P’字符。
4.足够k个‘R’字符且包含‘P’字符。
由此可见:上限是第一个‘P’字符,因为如果已经足够k个‘R’字符,情况就在达成条件的第k个‘R’到第一个‘P’之间。下限是第k个‘R’字符,假如在达到第k个‘R’前出现了‘P’,则不存在方案,假如没有出现,情况仍然在达成条件的第k个‘R’到第一个‘P’之间或字符串尾部。
因此,需要遍历原字符串的各个字符,然后从该字符开始对第k个‘R’二分,第一个‘P’二分,比对下标大小,累加以获得总方案数。
代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
const int M = 2e5+10;

int R[M];
int P[M];

int main()
{		
	int n,k;
	cin>>n>>k;
	string str1;
	cin>>str1;
	string str = " "+str1;
	for(int i=1;i<str.size();++i){
		if(str[i]=='R')R[i]=R[i-1]+1;
		else R[i]=R[i-1];
		if(str[i]=='P')P[i]=P[i-1]+1;
		else P[i]=P[i-1];
	}
//	for(int i=1;i<str.size();++i){
//		cout<<R[i]<<" "<<P[i]<<endl;
//	}
//	cout<<"-------------"<<endl;
	ll ans = 0;
	for(int i=1;i<str.size();++i){
		int l1 = i,r1 = str.size()-1;
		while(l1<r1){
            int mid=(l1+r1)>>1;			
			if(R[mid]-R[i-1]>=k){
				r1=mid;
			}else{
				l1=mid+1;
			}
		}
		int l2 = i,r2 = str.size()-1;
		while(l2<r2){
		    int mid=(l2+r2+1)>>1;
		    if(P[mid]-P[i-1]==0)
		    {
		        l2=mid;
		    }
		    else
		    {
		        r2=mid-1;
		    }			
		}
		int f1 = 1,f2 = 1;
		if(R[l1]-R[i-1]<k)f1=0;
		if(P[l2]-P[i-1]>0)f2=0;
//	    cout<<P[l2]<<" "<<P[i]<<endl; 
		if(l2-l1+1>0&&f1&&f2){
	        ans += l2 - l1 + 1;	
//	        cout<<1<<" ";		
		}

//		cout<<l1<<" "<<l2<<endl;
	}
	cout<<ans<<endl;
	return 0;
}

题解二: 思维,数组标记法
时间复杂度: O(n)
思路:
假设两个‘P’之间不少于k个‘R’,那么在第一个‘R’和第一个‘P’,第‘k’个‘R’和第二个‘P’之间的下标的笛卡尔积就是在两个‘P’所属区间内从第一个R开始的方案数,当然‘R’可能超过k个,那么第二个,第三个‘R’也需要进行这样的计算,当区间内‘R’的数量不小于k+1,k+2时。那样,就需要事先存下第二个,第三个‘R’的下标,当数到k+1,k+2个‘R’的时候,需要再次进行方案数的计算和累加。
例子:
字符串:PSDADRRRARRADAWP
k为4,n为16,第一个‘P’下标为0,第二个‘P’下标为15。
第一个‘R’下标为5,第4个‘R’下标为9,
方案数为(5-0) * (15-9)。
第二个‘R’下标为6,第5个‘R’下标为10,
方案数为(6-5) * (15-10)。
总方案数为5 * 6 + 1 * 5 = 35(种)。
由此可以推广到整个字符串的方案计算。
代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 7;
vector<int>v;
int n,k,cnt,pos1,pos2,snt,ans,t;
int p[N],q[N];//p[i] 存前方最近的边界,q[i]存后方最近的边界
string s;
signed main(){
    scanf(" %lld%lld",&n,&k);
    cin>>s;
    s = '0' + s;
    for (int i=1;i<=n;i++){
        if(s[i]=='P') snt = i;
        p[i] = snt;
    }
    snt = n + 1;
    for (int i=n;i>=1;i--){
         if(s[i]=='P') snt = i;
        q[i] = snt;
    }
    for (int i=1;i<=n;i++){
        if (s[i]=='P') cnt = 0,v.clear(),t=0;
        if (s[i]=='R'){
            v.push_back(i);
            cnt++;
            if (cnt==1){
                pos1 = v[t];//pos1标记第一个R的位置
            }
            if (cnt==k){
                pos2 = i;//pos2标记第k个R的位置
                ans += (pos1 - p[i]) * (q[i] - pos2);
            }
            else if (cnt>k){
                t++;pos1 = v[t];
                pos2 = i;//pos2标记第k个R的位置
                ans += (pos1 - v[t-1]) * (q[i] - pos2);
            }
        }
    }
    printf("%lld",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
牛客 a卷2022年第四季度的华为题目中,要求考生设计一种高效的数据结构,能够支持以下几种操作: 1. 添加一个元素 2. 删除一个元素 3. 查找是否存在某个元素 4. 返回元素的总数 该数据结构要求满足空间复杂度较小、时间复杂度较低、能够快速地进行查找和修改等多种操作。 想要编写这样一种数据结构,我们可以参考许多已有的经典算法与数据结构,如二叉树、哈希表、红黑树等,通过综合利用它们的优点来实现这个问题的解决。 例如,我们可以通过哈希表来存储所有元素的值,并在每个哈希链表的元素中再使用红黑树来进行排序与查找。这样,我们既能够轻松地进行元素的添加和删除操作,也能够在查找较大数据范围和数量时保持较高的速度与效率。同时,由于使用了多个数据结构来协同完成这个问题,我们也能够在空间复杂度上适度地进行优化。 当然,在具体设计这个数据结构的过程中,我们还需要考虑一些实践中的细节问题,例如如何避免哈希冲突、如何处理数据丢失与被删除元素所占用的空间等问题,这都需要相应的算法与流程来进行处理。 总体来看,设计这种支持多种操作的高效数据结构,需要我们具备丰富的算法知识和编程实践能力,同时需要我们在具体处理问题时能够将多种算法和数据结构进行有效地结合。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

某六十九岁合法萝莉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值