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;
}

浙公网安备 33010602011771号