题目描述 传送门
DP,设
d(i,j)
为前
i
段(
转移就枚举前一段以字符c结尾(如果有)且c!=j(因为如果c==j根据状态定义c要放在最后,虽然有多个c可以一些放在最前面与前一段合并,一些放在最后面满足状态定义,但总块数和全放在最后还是一样的),如果此段有c那将此段的c放在最前和前一段的c合在一起最好,总块数-1。
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
bool v[256];
int d[1010][256];
int main(){
int t;
cin>>t;
while(t--){
int k;
scanf("%d",&k);
char s[1010];
scanf("%s",s);
int len=strlen(s);
int l=len/k;
for(int i=0;i<l;i++){
memset(v,0,sizeof(v));
int cnt=0;
for(int j=i*k;j<i*k+k;j++)
if(!v[s[j]]) cnt++,v[s[j]]=1;
if(i==0){
for(int j='a';j<='z';j++){
if(v[j]) d[i][j]=cnt;
else d[i][j]=-1;
}
}
else{
for(int j='a';j<='z';j++){
d[i][j]=1e9;
if(v[j]){
for(int u='a';u<='z';u++){
if(d[i-1][u]>-1){
int o=0;
if(cnt==1&&u==j) o=1;
else if(v[u]&&u!=j) o=1;
d[i][j]=min(d[i][j],d[i-1][u]-o+cnt);
}
}
}
}
}
}
int ans=1e9;
for(int i='a';i<='z';i++) if(d[l-1][i]>-1) ans=min(ans,d[l-1][i]);
printf("%d\n",ans);
}
return 0;
}