最短字符编码

给定一个非空字符串, 按照如下方式编码, 使得编码后长度最小, 返回编码后的长度: 

编码规则为: 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;
}

 

链接:https://www.nowcoder.com/questionTerminal/5f76ce2a25744d96bb9797e03c523302?orderByHotValue=1&page=1&onlyReference=false
来源:牛客网
 

#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;
}

参考资料:https://www.cnblogs.com/grandyang/p/6194403.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值