题意:给一个正整数k和字符串s,s的长度是k的倍数,把s每k个字符分成一组,没组之间的字符可以任意重排,但组与组之间的顺序保持不变。
任务是让重排后的新字符串s'的块最少,连续相同的字符组成一个块,比如abbbaa有三个块a、bbb、aa。
思路见代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
//dp[i][j]表示将第i块的第j个字母放在本块的最后一个位置的最优解
char s[1010];
int vis[105];
int dp[1010][1010];
int main()
{
int t,n,len,k;
cin>>t;
while(t--)
{
memset(dp,0x3f3f3f3f,sizeof(dp));
cin>>k>>s;
len=strlen(s);
for(int i=0;i<len/k;i++) //共有len/k块
{
memset(vis,0,sizeof(vis));
int count=0; //计算本块的不同字母数
for(int j=i*k;j<(i+1)*k;j++)
{
if(vis[s[j]-'a'+1]==0)
{
vis[(s[j]-'a'+1)]=1;count++;
}
}
if(i==0) //dp值初始化,当i为0时,因为他是第一块所以他不管哪个字母放在块尾本块的最小值一定为不同字母数
{
for(int j=0;j<k;j++)
{
dp[i][j]=count;
}
continue;
}
for(int j=0;j<k;j++)
{
int rear=i*k+j;
for(int l=0;l<k;l++)
{
int pre=(i-1)*k+l; //当第i-1块第l个字母放块尾时
if(vis[s[pre]-'a'+1]&&(count==1||s[rear]!=s[pre])) //如果第i块有与第l个字母相同的字母时
{
dp[i][j]=min(dp[i][j],dp[i-1][l]+count-1);
}
else //如果不相同时
{
dp[i][j]=min(dp[i][j],dp[i-1][l]+count);
}
}
}
}
int ans=0x3f3f3f3f;
for(int i=0;i<k;i++)
{
ans=min(ans,dp[len/k-1][i]);
}
cout<<ans<<endl;
}
return 0;
}