CF1385D a-Good String

CF1385D a-Good String

轻松愉快的分支小练习~

发现一下性质:

1、字母变化具有一定的连续性。

2、长度为 \(2^i\) 的片段很关键。

3、对于确定的 \(n\) 可以直接判定其字母组成,而每个字母有前后两种填法。

对于一个长度为 \(2^i\) 的,所填字母为 \(val\) 的片段,显然贡献就是“不是 \(val\) 的字母数量”,这个可以用前缀和预处理后快速查询。

再根据性质3,就很有分治的感觉,于是可以从区间 \([1, n]\) 开始分治。对于区间 \([l, r]\),有前后两种填法:

1、\([l, mid]\)\(val\)\([mid + 1, r]\) 继续分治。

2、\([mid + 1, r]\)\(val\)\([l, mid]\) 继续分治。

两者取 min 就可以了。

时间复杂度 \(O(26n)\)

#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l); i <= (r); ++ i)
#define G(i,r,l) for(int i(r); i >= (l); -- i)
using namespace std;
using ll = long long;
const int N = 4e5;
int s[N][30];
int T, n;
char c[N];
namespace task{
	int solve(int l, int r, int val){
		if(l == r){
			if(c[l] - 'a' == val) return 0;
			return 1;
		}
		int mid = (l + r) / 2, len = (r - l + 1) / 2;
		return min(len - (s[mid][val] - s[l - 1][val]) + solve(mid + 1, r, val + 1), len - (s[r][val] - s[mid][val]) + solve(l, mid, val + 1));
	}
	void Main(){
		cin >> n;
		cin >> (c + 1);
		F(i, 1, n){
			F(j, 0, 25){
				if(c[i] - 'a' == j) s[i][j] = s[i - 1][j] + 1;
				else s[i][j] = s[i - 1][j];
			}
		}
		cout << solve(1, n, 0) << '\n';
	}
}
signed main(){
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> T;
	while(T --) task::Main();
	return fflush(0), 0;
}
posted @ 2025-08-04 14:18  superl61  阅读(9)  评论(0)    收藏  举报