D. Say No to Palindromes (cf) 前缀和

原题链接:Problem - 1555D - Codeforces

给你一串字符串,只由abc三个字母构成,告诉你字符串总长n, 需要查询次数m。给你m行,每行两个数字l,r(下标从1开始),让你找到这个区间上改变最小的字母个数使得这个区间上没有回文子串。

分析一下,要使一个字符串中没有子串是回文串,如果第一个字母是a,那么第二个子母可以选择b或c。如果第二个字母选b,那么第三个字母就不可能是a也不可能是b(bb是回文子串),所以第三个字母只能是c,同理第四个只能是a...所以一定是三个字母一循环的形式。

所以有六种可能:

1.abcabcabc...

2.acbacbacb...

3.bacbacbac...

4.bcabcabca...

5.cabcabcab...

6.cbacbacba...

所以我们可以枚举这六种作为我们要将这个子串改变的方向

我一开始做的时候没想到前缀和,每次一个询问就算一遍。其实也是没有想清楚其实并不需要每次区间都非要从abc、acb、、、这个顺序来对应,就比如说第一种abcabcabc...访问时区间左端点对应2,那么从2开始对应其实就改变了一下,类型变成了bcabca...其实从第2个位置作为左端点,这六种类型顺序(按照上面的1.2.3.4.5.6)都会改变,但是并不影响,因为每个类型还是会访问到而且我们只用取最小的那个数就可以了。

超时代码:

#include<iostream>
#include<cmath>
#include<vector>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
typedef pair<int, int> PII;
#define rep(i, n) for (int i = 1; i <= (n); ++i)
#define rrep(i, n) for (int i = m; i >= (1); --i)
typedef long long ll;
 
const int N = 200010;
string c[6] = { "abc", "acb", "bac","bca", "cab", "cba" };
vector<string> s0;
 
int main() {
	for (int i = 0; i < 6; i++) {
		string t;
		for (int j = 0; j < N; j++) {
			t += c[i][j % 3];
		}
		s0.push_back(t);
	}
	int n, m, l, r;
	string s;
	cin >> n >> m;
	cin >> s;
	while (m--) {
		cin >> l >> r;
		l--;
		int k = r - l;
		string t = s.substr(l,k);
		if (k == 1) {
			cout << 0 << endl;
			continue;
		}
		else if (k == 2) {
			if (t[0] == t[1]) cout << 1 << endl;
			else cout << 0 << endl;
			continue;
		}
		int ans = INF;
		for (int i = 0; i < 6; i++) {
			int sum = 0;
			for (int j = 0; j < k; j++) if (s0[i][j] != t[j]) sum++;
			ans = min(ans, sum);
		}
		cout << ans << endl;
	}
	return 0;
}

修改后的AC代码:

注意前缀和从1开始,但是之前的串都是从0开始存的,所以注意 j -1,之后取模那里也是对j - 1取模。

#include<iostream>
#include<cmath>
#include<vector>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
typedef pair<int, int> PII;
#define rep(i, n) for (int i = 1; i <= (n); ++i)
#define rrep(i, n) for (int i = m; i >= (1); --i)
typedef long long ll;
 
const int N = 200010;
int s[6][N];
char c[6][3] = { {'a', 'b', 'c'}, {'a', 'c', 'b'}, {'b', 'a', 'c'}, {'b', 'c', 'a'}, {'c', 'b', 'a'}, {'c', 'a', 'b'} };
char s0[N];
 
int main() {
	int n, m;
	scanf("%d %d", &n, &m);
	scanf("%s",s0);
	for (int i = 0; i < 6; i++) {
		for (int j = 1; j <= n; j++) s[i][j] = s[i][j - 1] + (s0[j - 1] != c[i][(j - 1) % 3]);
	}
	while (m--) {
		int l, r;
		scanf("%d %d", &l, &r);
		int ans = INF;
		for (int i = 0; i < 6; i++) {
			int sum = s[i][r] - s[i][l - 1];
			ans = min(ans, sum);
		}
		cout << ans << endl;
	}
	return 0;
}

还有大佬写的简洁代码:

当然因为我这个AC码是用C数组写的,比这个用string写的快一些

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值