思路
1.与当前i相关联的有 i,i+k,i+2k n-i+1, n-i+1-k,n-i+1-2k... ,题目就是让我们把这些位置上的字符变成一样,花费的最小值
2.考虑当前这个数a[i]的局部最优解,如何花费最小,贪心的想,肯定是把与它关联的 i,i+k,i+2k n-i+1, n-i+1-k... 这些字符改成这些字符串现次数最多的字符,(改成原次数出现最多的字符,就代表“这一轮一共修改的最少”),
3.考虑贪心有没有bug,(2)中局部求最优解,但这样贪心并不一定整体就最优,想一想这样局部求最优是否有无后效性。即当前选择的结果必须不能对之前的结果状态产生影响!
考虑i从1~n 肯定会出现判重复,对重复的数相关联的数再去修改就不能达到最优了!
想到可以设置标记数组,来标记之前是否修改过,因为前面修改过的数肯定是用和它相关联的数来修改的,已经 把与他相关联的 做了修正 这次就不用考虑他了,也不用再考虑与它相关联的数了。
代码
#include<bits/stdc++.h>
using namespace std;
int t,n,k,ans;
const int maxn = 2e5+100;
bool vis[maxn];
char s[maxn];
int cnt[30];
void init(){
ans = 0;
for(int i=1;i<=n;i++) vis[i] = false;
}
void solve(){
cin>>n>>k;
scanf("%s",s+1);
init();
for(int i=1;i<=n;i++){
if(vis[i]) continue;
for(int j=0;j<27;j++) cnt[j] = 0;
int cnt1 = 0;
int mx = 0;
//与当前i相关联的有 i,i+k,i+2k n-i+1, n-i+1-k...
//考虑从1~n判 肯定会出现判重复 设置vis数组标记用过
//因为前面用过的数肯定已经 把与他相关联的 做了修正 这次就不用考虑他了
for(int j=i;j<=n;j+=k){ //k个一组
if(!vis[j]){ //防止重复计算
vis[j] = true;
cnt1++;
cnt[s[j] - 'a']++;
mx = max(cnt[s[j] - 'a'],mx);
}else break;
}
for(int j=n-i+1;j>=1;j-=k){ //i对应的回文(另一端) k个一组
if(!vis[j]){
vis[j] = true;
cnt1++;
cnt[s[j] - 'a']++;
mx = max(cnt[s[j] - 'a'],mx);
}else break;
}
ans += cnt1 - mx;
}
cout<<ans<<endl;
}
int main(){
cin>>t;
while(t--){
solve();
}
return 0;
}