uva11552 字符串重排

关键词:字符串分组重排、最小块数

字符串从左到右每k个为一组(保证字符串长度是k的倍数),组内字符串可以任意移动位置,组间顺序不能变换。求变换后最少的块数。块的定义:连续相等的字符串为一个快

每一组为一个单位,因此dp时需要加入组作为一维,又发现计算加入新组后块的增量,需要知道上一组中最后一个字符是什么,因此状态设计为

dp[i][j]:前i组中,最后一个数是第i组的第j位字符的最小分块数。

dp[i][j]=min{ dp[i-1][l]+tmp,1<=l<=k }

tmp初始化为第i组中不同字符的个数,然后比较两组最后一个元素是否相等,经过处理后,tmp可能会减1。细节不作说明。

预处理第i组的不同元素个数及标记第i组存在哪些字符,不然会超时


#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define maxn 1100
#define INF 1<<29
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;

int t,k,len,m,dp[maxn][maxn],flag[maxn][30],num[maxn],ans;
char s[maxn];

void pre(){
    mem(flag,0),mem(num,0);
    for(int i=1;i<=m;i++){
        for(int j=1;j<=k;j++){
            if(!flag[i][s[(i-1)*k+j]-'a']){
                flag[i][s[(i-1)*k+j]-'a']=1;
                num[i]++;
            }
        }
    }
}

int main(){
    //freopen("a.txt","r",stdin);
    scanf("%d",&t);
    while(t--){
        scanf("%d",&k);
        scanf("%s",s+1);
        len=strlen(s+1),m=len/k;//一共m组
        pre();
        for(int i=1;i<=m;i++)
            for(int j=1;j<=k;j++)
                dp[i][j]=INF;
        for(int j=1;j<=k;j++) dp[0][j]=0;
        for(int i=1;i<=m;i++){
            for(int j=1;j<=k;j++){
                for(int l=1;l<=k;l++){
                    int tmp=num[i];
                    if(i!=1){
                        if(s[(i-1)*k+j]==s[(i-2)*k+l]){//得判断第i组的最后一个元素和第(i-1)组的最后一个元素的是否相等!
                            if(tmp==1) tmp=0;
                        }
                        else{
                            if(flag[i][s[(i-2)*k+l]-'a']) tmp--;
                        }
                    }
                    dp[i][j]=min(dp[i][j],dp[i-1][l]+tmp);
                }
            }
        }
        ans=INF;
        for(int j=1;j<=k;j++){
            ans=min(ans,dp[m][j]);
        }
        printf("%d\n",ans);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值