【训练题31:区间DP+处理字符串技巧】Folding | VUA 1630 | POJ 2176

Folding | POJ 2176

难度

− 747 2181 -\frac{747}{2181} 2181747

题意

  • 我们用 n u m ( s t r ) num(str) num(str) 表示一种压缩方式,表示串 s t r str str 连续出现 n u m num num
    比如: 3 ( A B ) = A B A B A B 3(AB)=ABABAB 3(AB)=ABABAB
  • 现在,给你一个串,问你怎么压缩,使得压缩后的长度最短。输出压缩后的串。
    A A A A A A A A A A B A B A B C C D → 9 ( A ) 3 ( A B ) C C D AAAAAAAAAABABABCCD\rightarrow9(A)3(AB)CCD AAAAAAAAAABABABCCD9(A)3(AB)CCD

数据范围

串长 ≤ 100 \le 100 100
串只包含大写英文字母

思路

  • 基本想一会就知道是区间 D P DP DP 的题。但是感觉有点棘手。
  • d p [ i ] [ j ] dp[i][j] dp[i][j] 表示下标 i i i 到下标 j j j 这一段子串的最短压缩串。

拼接转移

  • 很好想,就是从中间某个地方断开来,然后两边一拼求最短串。
  • d p [ i ] [ j ] = 最 短 串 { d p [ i ] [ k ] + d p [ k + 1 ] [ j ] } dp[i][j]=最短串\{dp[i][k]+dp[k+1][j]\} dp[i][j]={dp[i][k]+dp[k+1][j]}

压缩转移

  • 关键这个压缩有点神奇。
    在这里插入图片描述
  • 如果现在处理的原串是 A B C A B C ABCABC ABCABC,长度为 6 6 6 , 我们依次暴力判断该串是否由多个相同的子串组成
  • 字串长肯定整除原串长。暴力比较,我们可以像滑动窗口一样判断窗口长为子串长+1,我们每次就暴力比较窗口两端字符是否相同即可,相同的话窗口整体右移一格。不相同那肯定不符合要求了。

对了,数字直接转字符串可以使用 string = to_string(num) ,但是 POJ 不允许呀,自己写个呗。

然后,我们搞出了我们的代码:

代码

n n n 为原串长度
时间复杂度 : O ( n 3 ) O(n^3) O(n3)

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 150;

string ss;
string dp[MAX][MAX];

string to_str(int num){
    string t = "";
    while(num){
        char tmp = '0' + (num%10);
        t += tmp;
        num /= 10;
    }
    reverse(t.begin(),t.end());
    return t;
}

int main()
{
    cin >> ss;
    int ed = ss.size();

    for(int i = 0;i < ed;++i)dp[i][i] = ss[i];

    for(int L = 2;L <= ed;++L)
    for(int i = 0;i+L-1 < ed;++i){
        int j = i + L - 1;

        for(int T = 1;2 * T <= L;++T){
            if(L % T)continue;
            int st = i;
            int ed = i + T;
            while(ed <= j && ss[st] == ss[ed])st++,ed++;
            if(ed > j){
                int shu = L / T;
                dp[i][j] = to_str(shu) + "(" + dp[i][i+T-1] + ")";
                break;
            }
        }

        for(int k = i;k < j;++k){
            if(dp[i][j].size() > dp[i][k].size() + dp[k+1][j].size() || dp[i][j].size() == 0)
                dp[i][j] = dp[i][k] + dp[k+1][j];
        }
    }
    cout << dp[0][ed-1] << endl;
    return 0;
}

快乐交几发,结果就:
在这里插入图片描述
???这不是复杂度挺合理的吗?
仔细判断了一下,初步认为是String 太慢了!

改进

  • 不能用 S t r i n g String String ,那我们只能使用 c h a r [ ] char[] char[] 数组了。
    这个时候发现我们char数组基本操作都不会
String S,Tchar[] C,T
S=“abc”strcpy(C,“abc”)或者sprintf(C,“ABC”)
S=Tstrcpy(C,T)或者sprintf(C,"%s",T)
S=“abc”+Tstrcpy(C,“abc”) +strcat(C,T) 或者 sprintf(C,“abc%s”,T)
S=to_string(double/int t)sprintf(C,"%d",t)或者sprintf(C,"%f",t)

这个 s p r i n t f ( ) sprintf() sprintf() 函数真不错!
咳咳,注意一下 s p r i n t f ( ) sprintf() sprintf() 是给 c h a r [ ] char[] char[] 赋值的,和输出 p r i n t f ( ) printf() printf() 没啥关系、、

核心代码

n n n 为原串长度
时间复杂度 : O ( n 3 ) O(n^3) O(n3)
T i m e ( m s ) = 172 Time(ms)=172 Time(ms)=172
但是常数更小了!时间至少缩到原来的五分之一。。。

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 150;
const int INF = 0x3f3f3f3f;

char ss[MAX];
struct node{
    int len;
    char str[MAX];
}dp[MAX][MAX];

int main()
{
    scanf("%s",ss);
    int ed = strlen(ss);

    for(int i = 0;i < ed;++i)dp[i][i].str[0] = ss[i],dp[i][i].len = 1;

    for(int L = 2;L <= ed;++L)
    for(int i = 0;i+L-1 < ed;++i){
        int j = i + L - 1;
        dp[i][j].len = INF;

        for(int T = 1;2 * T <= L;++T){
            if(L % T)continue;
            int st = i;
            int ed = i + T;
            while(ed <= j && ss[st] == ss[ed])st++,ed++;
            if(ed > j){
                int shu = L / T;
                sprintf(dp[i][j].str,"%d",shu);
                strcat(dp[i][j].str,"(");
                strcat(dp[i][j].str,dp[i][i+T-1].str);
                strcat(dp[i][j].str,")");
                dp[i][j].len = strlen(dp[i][j].str);
                break;
            }
        }

        for(int k = i;k < j;++k){
            if(dp[i][j].len > dp[i][k].len + dp[k+1][j].len){
                strcpy(dp[i][j].str,dp[i][k].str);
                strcat(dp[i][j].str,dp[k+1][j].str);
                dp[i][j].len = strlen(dp[i][j].str);
            }
        }
    }
    printf("%s\n",dp[0][ed-1].str);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值