关键词:字符串分组重排、最小块数
字符串从左到右每k个为一组(保证字符串长度是k的倍数),组内字符串可以任意移动位置,组间顺序不能变换。求变换后最少的块数。块的定义:连续相等的字符串为一个快
每一组为一个单位,因此dp时需要加入组作为一维,又发现计算加入新组后块的增量,需要知道上一组中最后一个字符是什么,因此状态设计为
dp[i][j]:前i组中,最后一个数是第i组的第j位字符的最小分块数。
dp[i][j]=min{ dp[i-1][l]+tmp,1<=l<=k }
tmp初始化为第i组中不同字符的个数,然后比较两组最后一个元素是否相等,经过处理后,tmp可能会减1。细节不作说明。
预处理第i组的不同元素个数及标记第i组存在哪些字符,不然会超时
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define maxn 1100
#define INF 1<<29
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
int t,k,len,m,dp[maxn][maxn],flag[maxn][30],num[maxn],ans;
char s[maxn];
void pre(){
mem(flag,0),mem(num,0);
for(int i=1;i<=m;i++){
for(int j=1;j<=k;j++){
if(!flag[i][s[(i-1)*k+j]-'a']){
flag[i][s[(i-1)*k+j]-'a']=1;
num[i]++;
}
}
}
}
int main(){
//freopen("a.txt","r",stdin);
scanf("%d",&t);
while(t--){
scanf("%d",&k);
scanf("%s",s+1);
len=strlen(s+1),m=len/k;//一共m组
pre();
for(int i=1;i<=m;i++)
for(int j=1;j<=k;j++)
dp[i][j]=INF;
for(int j=1;j<=k;j++) dp[0][j]=0;
for(int i=1;i<=m;i++){
for(int j=1;j<=k;j++){
for(int l=1;l<=k;l++){
int tmp=num[i];
if(i!=1){
if(s[(i-1)*k+j]==s[(i-2)*k+l]){//得判断第i组的最后一个元素和第(i-1)组的最后一个元素的是否相等!
if(tmp==1) tmp=0;
}
else{
if(flag[i][s[(i-2)*k+l]-'a']) tmp--;
}
}
dp[i][j]=min(dp[i][j],dp[i-1][l]+tmp);
}
}
}
ans=INF;
for(int j=1;j<=k;j++){
ans=min(ans,dp[m][j]);
}
printf("%d\n",ans);
}
}