[hihoCoder] 压缩字符串 解题报告

本文介绍了如何运用分治策略(divide and conquer)解决hihoCoder上的一道字符串压缩题目。作者首先分析了三种不同情况:1) 字符串长度小于4时不压缩;2) 周期字符串可以直接压缩;3) 非周期字符串需枚举分割点。在实现算法过程中,为了避免重复工作,引入了记忆化搜索。最后提供了代码实现。
摘要由CSDN通过智能技术生成

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB

描述

小Hi希望压缩一个只包含大写字母'A'-'Z'的字符串。他使用的方法是:如果某个子串 S 连续出现了 X 次,就用'X(S)'来表示。例如AAAAAAAAAABABABCCD可以用10(A)2(BA)B2(C)D表示。

此外,这种压缩方法是可以嵌套的,例如HIHOHIHOCODERHIHOHIHOCODER可以表示成2(2(HIHO)CODER)。

对于一个字符串 S ,合法的压缩表示可能有很多种。例如AAAAAAAAAABABABCCD还可以表示成9(A)3(AB)CCD。小Hi希望知道其中最短的表示方法长度是多少。

输入

第一行一个正整数 T (1 ≤ T ≤ 10),表示测试数据的组数。

以下 T 行每行一个字符串 S ,长度不超过100。

输出

对于每组数据,输出最短的表示方法的长度。

样例输入
3
ABC
AAAAAAAAAABABABCCD
HIHOHIHOCODERHIHOHIHOCODER
样例输出
3
12
15

思路:刚看到这题的时候一点思路都没有,后来仔细观察了一下两种样例的分割方式之后隐约感觉像是dp的问题,但是又不好写状态转移方程.然后发现可以用divide and conquer来做.每次对于一个字符串有三种情况:

1.如果长度小于4就没必要再压缩了,因为不管怎么压缩长度至少增加3,所以肯定比原来的大.

2.如果当前字符串是周期的,就是如: hihocoerhihocoder这样可以完全分割成两个相等的子串,那么再就可以直接压缩.并且将其子串再进行压缩.需要注意一个特殊情况是长度为4的时候,只有完全为一个字母压缩才是必要的.如abab这样是没必要压缩的.

3.如果当前字符串不能直接压缩,那就需要将其分割.枚举每个分割点.

这样大致算法就是完成了,但是依然无法通过大数据,因为在分割的时候做了大量的重复工作,因此还需要记忆分割过的字符串,如果当前字符串以前已经搜索过了,就直接返回结果.

代码如下:

/*************************************************************************
 *  > File Name: cutstring.cpp
 *  > Author: Maoting Ren
 *  > Mail: mren@g.clemson.edu
 *  > Created Time: Sun 05 Jun 2016 03:31:21 AM EDT
 ************************************************************************/
#include<iostream>
#include<algorithm>
#include<string>
#include<limits.h>
#include<unordered_map>
using namespace std;

unordered_map<string, int> mem;
int divid(string str)
{
    if(str.size() < 4) return str.size();
    if(mem.count(str)) return mem[str];
    int len = str.size(), Min = INT_MAX;
    for(int i = 1, j; i <= len/2; i++)
    {
        string tem = str.substr(0, i);
        for(j = 0; j <= len-i; j+= i)
            if(str.substr(j, i) != tem) break;
        if(j >= len)
            return to_string(len/i).size() + 2 + divid(tem);
        if(len == 4) return 4;
    }
    for(int i = 1; i < len; i++)
    {
        string left = str.substr(0, i), right = str.substr(i); 
        int val1 = divid(str.substr(0, i)), val2 = divid(str.substr(i));
        if(left.size() > 3) mem[left] = val1;
        if(right.size() > 3) mem[right] = val2;
        Min = min(Min , val1+val2);
    }
    return Min;
}

int main()
{
    string str;
    int T;
    cin >> T;
    while(T--)
    {
        cin >> str;
        cout << divid(str) << endl;
    } 
    return 0;
}





 




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值