给定一个非空字符串, 按照如下方式编码, 使得编码后长度最小, 返回编码后的长度:
编码规则为: k[encoding_string], 表示重复k次encoding_strng,
例如'abcdefabcdefabc'可表示为'2[abcdef]abc', 但是'aaa'仅能编码成'aaa',
因为len('3[a]')>len('aaa').
补充:
1. k为正整数, []内的encoding_string不得含有空格不得为空;
2. []内的encoding_string 本身可以为编码过的字符串, 例如'abcdabcdeabcdabcde' 可以编码为 '2[abcdabcde]'(编码后长度从18减少到12), []内的'abcdabcde'又可以编码为 '2[abcd]e', 最终编码为 '2[2[abcd]e]', 编码后长度为11, 应返回11; 这个编码路径也能是: 'abcdabcdeabcdabcde' -> '2[abcd]e2[abcd]e' -> '2[2[abcd]e]';
2. 输入字符串为全小写英文字母, 长度<=160;
3. 如果编码后长度没有更小, 则保留原有字符串;
输入描述:
一行数据, 表示输入字符串
输出描述:
输出一个字符串表示编码后长度
输入例子1:
aaa
输出例子1:
3
例子说明1:
aaa,长度3
输入例子2:
aaaaa
输出例子2:
4
例子说明2:
5[a],长度4
输入例子3:
aabcaabcd
输出例子3:
8
例子说明3:
2[aabc]d,长度8
解题思路:我们建立一个二维的DP数组,其中dp[i][j]表示s在[i, j]范围内的字符串的缩写形式(如果缩写形式长度大于子字符串,那么还是保留子字符串),那么如果s字符串的长度是n,最终我们需要的结果就保存在dp[0][n-1]中,然后我们需要遍历s的所有子字符串,对于任意一段子字符串[i, j],我们我们以中间任意位置k来拆分成两段,比较dp[i][k]加上dp[k+1][j]的总长度和dp[i][j]的长度,将长度较小的字符串赋给dp[i][j],然后我们要做的就是在s中取出[i, j]范围内的子字符串t进行合并。合并的方法是我们在取出的字符串t后面再加上一个t,然后在这里面寻找子字符串t的第二个起始位置,如果第二个起始位置小于t的长度的话,说明t包含重复字符串,举个例子吧,比如 t = "abab", 那么t+t = "abababab",我们在里面找第二个t出现的位置为2,小于t的长度4,说明t中有重复出现,重复的个数为t.size()/pos = 2个,那么我们就要把重复的地方放入中括号中,注意中括号里不能直接放这个子字符串,而是应该从dp中取出对应位置的字符串,因为重复的部分有可能已经写成缩写形式了,比如题目中的例子5。如果t = "abc",那么t+t = "abcabc",我们在里面找第二个t出现的位置为3,等于t的长度3,说明t中没有重复出现,那么replace就还是t。然后我们比较我们得到的replace和dp[i][j]中的字符串长度,把长度较小的赋给dp[i][j]即可,时间复杂度为O(n3),空间复杂度为O(n2)
代码如下:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
string encode(string s) {
int n = s.size(); //求出s的大小,用于定义数组大小
vector<vector<string>> dp(n, vector<string>(n, "")); //定义了一个二维数组dp
for (int step = 1; step <= n; ++step) {
for (int i = 0; i + step - 1 < n; ++i) {
int j = i + step - 1;
dp[i][j] = s.substr(i, step); //将从i到step的字符赋给dp[i][j]
for (int k = i; k < j; ++k) {
string left = dp[i][k], right = dp[k + 1][j];
if (left.size() + right.size() < dp[i][j].size()) {
dp[i][j] = left + right;
}
}
string t = s.substr(i, j - i + 1), replace = "";
auto pos = (t + t).find(t, 1);
if (pos >= t.size()) replace = t;
else replace = to_string(t.size() / pos) + '[' + dp[i][i + pos - 1] + ']';
if (replace.size() < dp[i][j].size()) dp[i][j] = replace;
}
}
return dp[0][n - 1];
}
int main()
{
string s;
cin >> s;
string result = "";
result = encode(s);
cout << result.size() << endl;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
string encoding_string(string s)
{
if (s == "")return "";
if (s.size() <= 4)return s;
int len = s.size();
int len2 = len / 2; //重复子串的最大长度 可以分成的份数至少要2份
int best_count = 0;//一次遍历得到的最优重复数
int best_len = INT_MAX;//一次遍历得到的最优压缩到的长度
string pre, cur, lat;//一次遍历得到的最优子串
while (len2 >= 1)//重复子串长度最小为1
{
for (int k = 0; k <= len - len2; k++)//从第k个下标开始找重复子串
{
int count = 1;
string s2 = s.substr(k, len2);
string s3, s4;
for (int j = 1; len2 * j + len2 <= len; j++)
{
s3 = s.substr(k + len2 * j, len2);
if (s2.compare(s3) == 0 && s2.size() == s3.size())
count++;
else
break;
}
int newlen = (len - count * len2) + 3 + len2;//压缩后的字符串长度
if (newlen < len && newlen < best_len && count > 1)//如果压缩有效
{
best_len = newlen;
best_count = count;
pre = s.substr(0, k);
cur = s.substr(k, len2);
lat = s.substr(k + count * len2);
}
}
len2--;//重复字符串长度缩短1
}
if (best_count == 0)
return s;
return encoding_string(pre) + to_string(best_count) + "[" + encoding_string(cur) + "]" + encoding_string(lat);
}
int main()
{
string s;
cin >> s;
string result = "";
result = encoding_string(s);
cout << result.size() << endl;
return 0;
}