题目大意:
给出一个数字k和一串字符,k个字母为一个子串,连续的相同的字母组成一个chunks,求字符串中最少的chunks个数。
思路:
DP。
dp[i][j]表示的第i个子串中第j个字母作为最后一个字母最少可以有多少个chunks.
当第i-1个字串中出现了与第i个字串中相同的字母并且第i个字串只有一个chunks或者第i个子串的最后一个字母跟第i-1个字串的最后一个字母不相等的话,那么
dp[i][j] = min(dp[i][j],dp[i - 1][k] + chunks - 1);
否则
dp[i][j] = min(dp[i][j],dp[i - 1][k] + chunks);
代码:
#include <iostream>
using namespace std;
#include <cstring>
#include <stdio.h>
char str[1005];
const int INF =0x3f3f3f3f;
int dp[1010][1010];
bool vis[130];//注意vis数组的大小。。因为这个问题错了还不知道在哪里错了。。
int main() {
int T;
int k;
scanf("%d",&T);
while(T--) {
scanf("%d",&k);
scanf("%s",str);
int len = strlen(str);
memset(dp,INF,sizeof(dp));
for(int i = 0 ; i < len/ k; i++) {
int chunks = 0;
memset(vis,0,sizeof(vis));
for(int j = i * k; j <=(i+ 1)*k - 1; j++)//第i块有哪些字母
vis[str[j]] = true;
for(int j = 'a'; j <= 'z'; j++)//计算出不同的字母个数
if(vis[j])
++chunks;
if(i == 0) {//如果是第一块的话那么以每一个字母结尾都是chunks
for(int j = 0; j <k ;j++)
dp[i][j] = chunks;
continue;
}
for(int j = 0; j < k; j++) {
int rear = i * k + j;//枚举第i块中的所有字母
for(int p = 0; p < k; p++) {
int pre = (i-1) * k + p;//枚举第i - 1块中所有的字母
if(vis[str[pre]] && (chunks == 1 || str[pre] != str[rear]))//如果i - 1块中的字母在i中出现过而且i的不同字母个数只有一个或者i最后一个字母不合i - 1的最后一个字母相等的话那么就可以有相同的块就可以减1
dp[i][j] = min(dp[i][j],dp[i - 1][p] + chunks - 1);
else
dp[i][j] = min(dp[i][j],dp[i - 1][p] + chunks);
}
}
}
int ans = INF;
for(int i = 0; i < k; i ++) {
ans = min(ans,dp[len / k - 1][i]);
//printf("%d ",dp[len / k - 1][i]);
}
printf("%d\n",ans);
}
return 0;
}