https://acm.uestc.edu.cn/status/78088
设dp[i][j]表示当前处理到第i块, 第i块的末尾放第i块中的第j个字母 的时候,1~i块之间的段数.
转移时枚举i-1块的结尾l和i块结尾j,如果i块 没有这个l,那么dp[i][j]只能是dp[i-1][l]加 上第i块的所有不同字母数 否则,可以把第i块的l放到第一段,这样 dp[i][j]=dp[i-1][l]+i块不同字母数-1
显然l代表的字母等于j代表的时候不能这样 转移,除非第i块有且只有j这一个字母
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e7+9;
const int INF = 0x3f3f3f3f;
int dp[1009][1009];
char s[1009];
int a[1009];
int p[1009][26];
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(p,0,sizeof(p));
memset(dp,0x3f,sizeof(dp));
int k;scanf("%d",&k);
scanf("%s",s);
int len=strlen(s);
int num=0;
for(int i=0;i<len;i++){
if(p[i/k+1][s[i]-'a']==0){
num++;
}
p[i/k+1][s[i]-'a']++;
if((i+1)%k==0){
a[i/k+1]=num;
num=0;
}
}
for(int i=0;i<26;i++){
if(p[1][i]){
dp[1][i]=a[1];
}
}
for(int i=2;i<=len/k;i++){
for(int j=0;j<26;j++){
if(p[i][j]){
for(int l=0;l<26;l++){
if(dp[i-1][l]==0)continue;
if(l!=j){
if(p[i][l]&&dp[i][j]>dp[i-1][l]+a[i]-1){
dp[i][j]=dp[i-1][l]+a[i]-1;
}
if(!p[i][l]&&dp[i][j]>dp[i-1][l]+a[i]){
dp[i][j]=dp[i-1][l]+a[i];
}
}
else{
if(a[i]==1&&dp[i][j]>dp[i-1][l]){
dp[i][j]=dp[i-1][j];
}
}
}
}
}
}
int ans=INF;
for(int i=0;i<26;i++){
if(dp[len/k][i]){
ans=min(ans,dp[len/k][i]);
}
}
printf("%d\n",ans);
}
return 0;
}