题目大意
给定以个正整数n和字符串 S,字符串的长度保证为k的整数倍. 把S的字符按照从左向右的顺序没k个分为一组,每组之间可以任意重排,但是组与组之间的顺序不可以调换,你的任务是让重排后的字符串包含尽量少的"块",其中连续的相同字符为一块。
分析
典型的动态规划,
我们设状态为d(i,j)表示前i个块以第i个块中的第j个字符结尾所包含的最小的块数.
转移方程为分为三种 注:coun为第i块字符数
1 对于第i个块,如果只有一种字母
d(i,j)=min{d(i-1,k)+k!=j}
2 如果有两种或两种以上的字母 那么
如果 第i快中不存在j个字符,d(i,j)=max(d(i-1,k)+coun)
如果存在 d(i,j)=max{d(i-1,k)+coun+(chpre==chj)-1}
代码如下
///*
#include<iostream>
#include<algorithm>
#include<string.h>
#define maxn 1000+10
#define Inf 0x3f3f3f3f
using namespace std;
char q[maxn];
int n, w;
int d[maxn][maxn];
int mark[27],num;
int lo(int i, int j) //定位当前处理字符的位置
{
return (i-1) * w + j;
}
int main()
{
int t;
cin >> t;
while (t--) {
cin >> w;
cin >> q+1;
int n = strlen(q + 1);
num = n / w;
int coun = 0;
memset(d, Inf, sizeof(d));
for (int i = 1; i <= num; i++) { //循环每一组;
int inti = (i - 1)*w + 1;
coun = 0;
memset(mark, 0, sizeof(mark));
for (int j = inti; j <inti+w; j++)
mark[q[j] - 'a' + 1] = 1;
for (int i = 1; i <= 26; i++)
if (mark[i]) coun++;
if (i == 1) {
for (int j = 1; j <= w; j++)
d[i][j] = coun;
continue;
}
for (int j = 1; j <= w; j++)
{
for (int pre = 1; pre <= w; pre++) {
char chj = q[lo(i, j)];
char chpre= q[lo(i - 1, pre)];
int m = mark[q[lo(i - 1, pre)]-'a'+1];
if (coun == 1 && m)
d[i][j] = min(d[i][j], d[i - 1][pre]);
else if (!m) //如果不存在字符chpre
d[i][j] = min(d[i][j], d[i - 1][pre] + coun-1);
else //如果存在字符chpre
d[i][j] = min(d[i][j], d[i - 1][pre] + coun+(chpre==chj)-1);
}
}
}
int ans = Inf;
for (int j = 1; j <= w; j++)
ans = min(ans, d[num][j]);
cout<<ans<<endl;
}
return 0;
}