题意:将一个字符串分成若干块,每块大小为k,保证串的长度是k的倍数。
每块的元素可以任意摆放,但是块与块之间的位置不变。摆放完后,将连续相同的字符看成一个,问最少多少个。
分析:定义dp[i][j]为第i块以该块的第j个元素结尾。
状态转移:
dp[i][j]=min(dp[i][j],dp[i-1][p]+cnt-1);//如果前一块以p结尾,而第i块不以p结尾,那么这里就可以合并一种字符。
dp[i][j]=min(dp[i][j],dp[i-1][p]+cnt);//其他不能合并情况。
cnt是当前字符种数。
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define Mn 1005
#define Mm 2000005
#define mod 1000000007
#define CLR(a,b) memset((a),(b),sizeof((a)))
#define CPY(a,b) memcpy ((a), (b), sizeof((a)))
#pragma comment(linker, "/STACK:102400000,102400000")
#define ul u<<1
#define ur (u<<1)|1
using namespace std;
typedef long long ll;
int dp[Mn][Mn];
int vis[30];
int main() {
int t;
string s;
scanf("%d",&t);
while(t--) {
int k;
scanf("%d",&k);
cin>>s;
CLR(dp,0x3f);
int block=s.size()/k;
for(int i=0;i<block;i++) {
CLR(vis,0);
int cnt=0;
for(int j=i*k;j<(i+1)*k;j++) vis[s[j]-'a']=1;
for(int j=0;j<26;j++) if(vis[j]) cnt++;
if(!i) for(int j=0;j<k;j++) dp[i][j]=cnt;
else for(int j=0;j<k;j++) {
for(int p=0;p<k;p++) {
if(vis[s[(i-1)*k+p]-'a']&&(s[i*k+j]!=s[(i-1)*k+p]||cnt==1))
dp[i][j]=min(dp[i][j],dp[i-1][p]+cnt-1);
else dp[i][j]=min(dp[i][j],dp[i-1][p]+cnt);
}
}
}
int ans=INF;
for(int i=0;i<k;i++) ans=min(dp[block-1][i],ans);
cout<<ans<<endl;
}
return 0;
}