字符串思维题练习 DAY4(CF 1849 C , CF 518A , CF1321C , CF1527 C)

字符串思维题练习 DAY4(CF 1849 C , CF 518A , CF1321C , CF1527C)

Problem - C - Codeforces

CF1849 C. Binary String Copying(思维)

大意:给出一个二进制串 , q 次操作 , 每次操作对这个串的一个区间[L , R]排序 , 求 q 次操作后产生的不同的字符串个数。

思路:给出两个思路:

1: 考虑哈希 , 每次操作后串的哈希值 = [0 , L - 1] 的哈希值 + 排序后[L , R] 的哈希值 + [R + 1 , n] 的哈希值 , 预处理之后可以把每次操作的哈希做到O(1)

时间复杂度 O ( n + q ) 时间复杂度O(n + q) 时间复杂度O(n+q)

由于 哈希容易被 hack , 所以不放哈希的代码了

2. 考虑每次操作是否改变了原串 , 即排序区间是否是有序的 , 若排序区间是有序的 , 区间排序后生成的串就是原串 , 若区间是无序的 , 我们考虑当前区间的本质区间是多少 , 何为本质区间 , 即当前区间 和 当前区间左端加无数个 0 , 右端加 无数个 1 本质上是相同的 , 因为排序后得到的串相同 , 我们要计算这个最大的区间 ,即为本质区间 , 用set 维护即可。

1. 如何判断一个区间是否排序?处理区间 0 的个数即可 , 前缀和处理

2. 如何O(1)的计算本质区间?预处理每个数左端连续 0 的个数和右端连续 1 的个数即可

时间复杂度 O ( n + q l o g q ) 时间复杂度O(n+qlogq) 时间复杂度O(n+qlogq)

#include
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int long long
const int INF = 9e18;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

int n , m , t;
string s , res;

/*
000100010
*/

signed main(){

	IOS
	cin >> t;
		
	while(t --) {
		set<pair<int , int>>st;
		cin >> n >> m >> s;
		s = '?' + s;
		vector<int>cnt(n + 1) , cntl(n + 1) , cntr(n + 1);
		for(int i = 1 ; i <= n ; i ++) {
			cnt[i] = cnt[i - 1] + (s[i] == '0');
		}
		int now = 0;
		for(int i = 1 ; i <= n ; i ++) {
			cntl[i] = now;
			if(s[i] == '1') now = 0;
			if(s[i] == '0') now += 1;
		}
		now = 0;
		for(int i = n ; i >= 1 ; i --){
			cntr[i] = now;
			if(s[i] == '1') now += 1;
			if(s[i] == '0') now = 0;
		}
		int l , r;
		
		for(int i = 1 ; i <= m ; i ++) {
			cin >> l >> r;
			int cntt = cnt[r] - cnt[l - 1];
			if(cntt == cnt[l - 1 + cntt] - cnt[l - 1]) {
				st.insert({-1 , -1});
			} else {
				st.insert({l - cntl[l] , r + cntr[r]});
			}
		}
		
		cout << st.size() << "\n";
		
	}
	
	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

CF 518 A. Vitaly and Strings(思维)

Problem - A - Codeforces

大意:给出两个字符串 s 和 t , 且 s 字典序小于 t , 求是否存在一个字符串字典序大于 s 且 小于 t。

思路:求出比 s 字典序 大 1 的字符串 ,如果等于 t , 不存在 , 否则输出即可。

#include
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int long long
const int INF = 9e18;
const int N = 2e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

string s , t;

signed main(){

	cin >> s >> t;
	int n = s.size();
	
	for(int i = n - 1 ; i >= 0 ; i --) {
		if(s[i] == 'z') {
			s[i] = 'a';
		} else {
			s[i] = char(s[i] + 1);
			break;
		}
	}
	
	if(s == t) {
		cout << "No such string\n";
	} else {
		cout << s << "\n";
	}

	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

CF 1321 C. Remove Adjacent

Problem - C - Codeforces

大意:给出一个字符串 s ,全是小写字母 , 每次操作 可以移除字符 si , 前提是 si-1 或 si+1 是 si 的前驱 ,求最大操作次数。|s| ≤ 100;

思路:首先考虑贪心的先删除字典序大的字母 , 因为先删除小的有后效性 , 其次考虑用数组模拟链表维护删除。由于|s| ≤ 100 , 暴力删除即可

时间复杂度 O ( n 2 ) 时间复杂度O(n^2) 时间复杂度O(n2)

注意由于删除了小的会对大的有影响 , 所以每次删完一个之后都要重新从最大的开始检查删除。

#include
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int long long
const int INF = 9e18;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

int n ;
string s;
bool vis[N];
vector<int>pos[30];
int pre[N] , nex[N];

signed main(){

	cin >> n >> s;
	s = '?' + s;
	for(int i = 1 ; i <= n ; i ++) pre[i] = (i == 1) ? -1 : i - 1; 
	for(int i = n ; i >= 1 ; i --) nex[i] = (i == n) ? -1 : i + 1;
	for(int i = 1 ; i <= n ; i ++) pos[s[i] - 'a' + 1].push_back(i);
	
	int cnt = n;
	
	while(cnt) {
		bool tag = 0;
		for(int i = 26 ; i >= 2 ; i --) {
			int j = pos[i].size();
			for(int k = 0 ; k < j ; k ++) {
				int now = pos[i][k];
				if(vis[now]) continue;
				if(pre[now] != -1 && s[pre[now]] + 1 == s[now]) {
					nex[pre[now]] = nex[now];
					pre[nex[now]] = pre[now];
					vis[now] = 1;
					cnt -= 1;
					tag = 1;
					continue;
				}
				if(nex[now] != -1 && s[nex[now]] + 1 == s[now]) {
					nex[pre[now]] = nex[now];
					pre[nex[now]] = pre[now];
					vis[now] = 1;
					cnt -= 1;
					tag = 1;
					continue;
				}
			}
		}
		if(!tag) break;
	}
	
	cout << n - cnt << "\n";

	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

CF 1527 C. Sequence Pair Weight

Problem - C - Codeforces

大意:给出一个字符串 S , 求

∑ 1 < = l < r < = n ∑ l < = x < y < = r ( a x = a y ) \sum_{1<=l1<=l<r<=nl<=x<y<=r(ax=ay)

思路:考虑同一个字母 x , 区间[x , y] 能做出的贡献是 x * (n - y + 1) , 但是枚举每一个区间显然时间复杂度是O(n^2)的 , 考虑前缀和优化 , 对于每一个右边界 , 其左边界的贡献显然是可以用前缀和进行维护的 , 用一个 map 维护左边界前缀和即可。

时间复杂度 O ( n l o g n ) 时间复杂度O(nlogn) 时间复杂度O(nlogn)

#include
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int long long
const int INF = 9e18;
const int N = 2e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;

int t , n , now;

signed main(){

	IOS
	cin >> t;
	
	while(t --) {
		cin >> n;
		int res = 0;
		map<int,int>pre;
		for(int i = 1 ; i <= n ; i ++) {
			cin >> now;
			res += pre[now] * (n - i + 1);
			pre[now] += i;
		}
		cout << res << "\n";
	}

	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

你可能感兴趣的:(算法)