题目地址:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2547
题目意思:
给你一个字符串,长度为k的整数倍,要你分成每个长度的段
每个段内可以重新编排
连续的几个字母看作一个块
问最少有几个块
解题思路:
对于每个段来说,我们可以知道最少的块,即里面有几种字母,记为chunk[i]
我们设f[i][j]为第i段的第j位放在最末尾时的最少块数
则针对第i-1块的第l个放在末尾时来说
如果和第i的第一个相同,则可以合并一个块,则
f[i][j] = min(f[i][j],f[i-1][l]+chunk-1);
否则
f[i][j] = min(f[i][j],f[i-1][l]+chunk);
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; const int maxn = 1010; int f[maxn][maxn];
char s[maxn];
bool vis[maxn]; int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int k,len;
scanf("%d",&k);
scanf("%s",s);
len = strlen(s);
memset(f,0x3f3f3f3f,sizeof(f));
for(int i=1;i<=len/k;i++)
{
memset(vis,false,sizeof(vis));
for(int j=1;j<=k;j++)
{
vis[s[(i-1)*k+j-1]] = true;
}
int chunk = 0;
for(int j='a';j<='z';j++)
if(vis[j])
chunk++;
if(i==1)
{
for(int j=1;j<=k;j++)
f[1][j] = chunk;
continue;
} for(int j=1;j<=k;j++)
{
int rear = (i-1)*k+j-1;
for(int l=1;l<=k;l++)
{
int pre = (i-2)*k+l-1;
if(vis[s[pre]] && (chunk==1 || s[pre]!=s[rear]))
f[i][j] = min(f[i][j],f[i-1][l]+chunk-1);
else
f[i][j] = min(f[i][j],f[i-1][l]+chunk);
}
}
} int ans = 0x3f3f3f3f;
for(int i=1;i<=k;i++)
ans = min(ans,f[len/k][i]);
printf("%d\n",ans);
}
return 0;
}