做题记录 牛客寒假基础训练3-I

题目链接

此题我原本的思路是:建立前缀和数组t[],表示前i个字符的种类总数。对于任意的i,在i+l-1之后找到第一个j使得j>=i+2即可。注意j之后剩下的字符数是否大于等于r。
但这种做法是错误的。考虑IE$abQ这样的字符串,这种方法会少统计$abQ从而得到错误答案。 看起来比起从第一个字符开始有多少种类型,我们似乎更应关心从i开始有多少类型。
因此将t数组的定义改为:从i开始,第一次遇到第j,(j∈[0,4))种类型的位置是哪儿。最后找到t[]中第三大的数对应的位置即可。这一过程可以用std::nth_element完成。
为了 完成上述工作,我们需要一个二维数组记录各种字符出现的位置。

实现中有一些细节,在代码中有注释。

#include <iostream>
#include <algorithm>
#include <cctype>
#include <vector>
const int M=100001;
typedef long long llong;
using namespace std;
char s[M];
vector<int> ch[4];
void getTypes(int n) {
	for(int i=0; i<n; i++) {
		if(isdigit(s[i])) {
			ch[0].push_back(i);
		}
		else if(islower(s[i])) {
			ch[1].push_back(i);
		}
		else if(isupper(s[i])) {
			ch[2].push_back(i);
		}
		else {
			ch[3].push_back(i);
		}
	}
	for(int i=0; i<4; i++)    ch[i].push_back(n);
}

int main() {
	ios::sync_with_stdio(false);
	//0,1,2,3分别代表数字、小写、大写、其他
	static int nex[4];
	int n,l,r;
	llong res=0;
	cin>>n>>l>>r;
	cin.getline(s,1);
	cin.getline(s,n);
	//小优化
	if(r>=3) {
		ch[0].reserve(n/3),ch[1].reserve(n/3),ch[2].reserve(n/3),ch[3].reserve(n/3);
		getTypes(n);
		for(int i=0; i<n; i++) {
			for(int j=0; j<4; j++) {
				//有可能在i+l-1之前就有三个了
				nex[j]=lower_bound(ch[j].begin(),ch[j].end(),i)-ch[j].begin();
				nex[j]=ch[j][nex[j]];
			}
			nth_element(nex,nex+2,nex+4);
			int third=nex[2],term=min(n,r+i);
			//由于有l,有贡献区间起始有两种情况:1.third 2.i
			int nowRes=min(term-third,term-(l+i-1));
			/* 注意
				1:对于上面两种情况,区间终止都要考虑两种情况(对应L45 term) 
				2:由于有r,区间可能并不产生贡献
			*/
			res+=(llong)max(nowRes,0);
		}
	}
	cout<<res;
	return 0;
}
上一篇:反转字符串II Java


下一篇:栈系列(洛谷刷题记录)