题意是将一字符串划分为若干组,组内可以重排位置,求最后最小包含的块数(即相同字母组成的连续字符串的个数)
这题是序列划分模型,有点类似于 商人那道题,主要是状态的设计,另dp(i,j)为第i个序列首字为j所能划分最小组数,可以很容易得到状态方程 ps额一开始脑洞开得有点大,各种边界没考虑清楚,需要注意的是,假如第i个序列以j开头,并不一定第i-1个序列以j结尾答案就最小,因为我们不能以局部来代替整体,这也是一开始一直wa的原因(泪目)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
using namespace std;
#define LL long long
const int maxn=1005;
const int INF=1000000000;
int d[maxn][30];
set<int> str[maxn];
int main(){
int t;
scanf("%d",&t);
while(t--){
int k;scanf("%d",&k);
string s;cin>>s;
int len=s.length();
int grp=len/k;
for(int i=1;i<=grp;i++) str[i].clear();
for(int i=1;i<=grp;i++){
for(int j=0;j<k;j++) str[i].insert(s[(i-1)*k+j]-'a');
}
for(int i=1;i<=grp;i++){
int sizei=str[i].size();
for(set<int>::iterator j=str[i].begin();j!=str[i].end();j++){
if(i==1){d[i][*j]=sizei;continue;}
d[i][*j]=INF;
if(str[i-1].count(*j)){
if(str[i-1].size()!=1)
for(set<int>::iterator k=str[i-1].begin();k!=str[i-1].end();k++){
if(*k!=*j) d[i][*j]=min(d[i][*j],d[i-1][*k]+sizei-1);
else d[i][*j]=min(d[i][*j],d[i-1][*k]+sizei);
}
else d[i][*j]=d[i-1][*j]+sizei-1;
}
else{
for(set<int>::iterator k=str[i-1].begin();k!=str[i-1].end();k++){
d[i][*j]=min(d[i][*j],d[i-1][*k]+sizei);
}
}
}
}
int ans=INF;
for(set<int>::iterator i=str[grp].begin();i!=str[grp].end();i++)
ans=min(ans,d[grp][*i]);
printf("%d\n",ans);
}
return 0;
}