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