-
3 ABC AAAAAAAAAABABABCCD HIHOHIHOCODERHIHOHIHOCODER
样例输出
-
3 12 15
描述
小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。
输出
对于每组数据,输出最短的表示方法的长度。
思路:刚看到这题的时候一点思路都没有,后来仔细观察了一下两种样例的分割方式之后隐约感觉像是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;
}