POJ 2176 Folding (字符串)

题意:个你一个字符串将其进行压缩,使得压缩后的串长度最短。

压缩规则:

  • A sequence that contains a single character from 'A' to 'Z' is considered to be a folded sequence. Unfolding of this sequence produces the same sequence of a single character itself. 
  • If S and Q are folded sequences, then SQ is also a folded sequence. If S unfolds to S' and Q unfolds to Q', then SQ unfolds to S'Q'. 
  • If S is a folded sequence, then X(S) is also a folded sequence, where X is a decimal representation of an integer number greater than 1. If S unfolds to S', then X(S) unfolds to S' repeated X times.
例如:AAAAAAAAAABABABCCD 压缩为 10(A)2(BA)B2(C)D


题解:区间DP,类似于矩阵链乘。对每个区间进行处理的时候,相当于对该区间进行打包,记录下该区间的基本模式串,以及该基本模式串的重复次数,例如:AAAAA => 5(A),基本模式串是"A",重复次数为5。我们应该尽量取重复次数多的打包方式,这样可以最大限度的与左右区间进行合并从而减少字符串的总长度。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

struct Segment
{
    int l, r; //l是基本模式串的长度,r是重复次数
    char s[101]; //基本模式串
} dp[101][101];

int diglen(int num) 
{
    if(num < 10) return 1;
    else if(num < 100) return 2;
    else return 3;
}

Segment pack(const Segment &seg) //进行打包
{
    Segment ret;
    int digNum = diglen(seg.r); //求数字的字符个数
    if(seg.l * seg.r < seg.l + digNum + 2) //+2是因为括号有两个字符
    {
        ret.r = 1;
        ret.l = seg.l * seg.r;
        strcpy(ret.s, seg.s);
        for(int i = 1; i < seg.r; i++)
            strcat(ret.s, seg.s);
    }
    else
    {
        ret.r = 1;
        ret.l = seg.l + digNum + 2;
        sprintf(ret.s, "%d(%s)", seg.r, seg.s);
    }
    return ret;
}

int packlen(const Segment &seg) //返回打包后的最小长度
{
    return min(seg.l * seg.r, seg.l + 2 + diglen(seg.r));
}

int linklen(const Segment &seg1, const Segment &seg2, int &linkr) //返回将两个区间合并后的最小长度
{
    int len;
    if(seg1.l == seg2.l && 0 == strcmp(seg1.s, seg2.s)) //基本模式串一样则只需将重复次数相加
    {
        linkr = seg1.r + seg2.r;
        len = seg1.l;
    }
    else 
    {
        linkr = 1;
        len = packlen(seg1) + packlen(seg2);
    }
    return min(linkr * len, len + 2 + diglen(linkr));
}

Segment link(const Segment &seg1, const Segment &seg2) //将两个区间的串进行合并
{
    Segment ret;
    if(seg1.l == seg2.l && 0 == strcmp(seg1.s, seg2.s))
    {
        strcpy(ret.s, seg1.s);
        ret.l = seg1.l;
        ret.r = seg1.r + seg2.r;
    }
    else //基本模式串不同则需要先分别对两个区间进行打包,然后连接起来
    {
        Segment tmp1 = pack(seg1); 
        Segment tmp2 = pack(seg2);
        strcpy(ret.s, tmp1.s);
        strcat(ret.s, tmp2.s);
        ret.l = tmp1.l + tmp2.l;
        ret.r = 1;
    }
    return ret;
}


int main()
{
    char str[110];
    while(scanf("%s",str) != EOF)
    {
        int i, j, k;
        int len = strlen(str);
        for(i = 0; i < len; i++)
        {
            dp[i][i].l = dp[i][i].r = 1;
            dp[i][i].s[0] = str[i];
            dp[i][i].s[1] = '\0';
        }

        int tmpl, tmpr;
        int markl, markr, markj;
        for(k = 1; k < len; k++)
        {
            for(i = 0; i + k < len; i++)
            {
                markj = i;
                markl = linklen(dp[i][i], dp[i+1][i+k], markr);
                for(j = i + 1; j < i + k; j++)
                {
                    tmpl = linklen(dp[i][j], dp[j+1][i+k], tmpr);
                    if(tmpl < markl || (tmpl == markl && tmpr > markr))
                        markl = tmpl, markr = tmpr, markj = j;
                }
                dp[i][i+k] = link(dp[i][markj], dp[markj+1][i+k]);
            }
        }
        Segment tmp = pack(dp[0][len-1]);
        printf("%s\n",tmp.s);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值