题意:将一个字符串分成若干块,每块大小为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; }