题目描述
给定一个非空字符串, 按照如下方式编码, 使得编码后长度最小, 返回编码后的长度: 编码规则为: k[encoding_string], 表示重复k次encoding_strng, 例如’abcdefabcdefabc’可表示为’2[abcdef]abc’, 但是’aaa’仅能编码成’aaa’, 因为len(‘3[a]’)>len(‘aaa’).
补充:
- k为正整数, []内的encoding_string不得含有空格不得为空;
- []内的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]’;
- 输入字符串为全小写英文字母, 长度<=160;
- 如果编码后长度没有更小, 则保留原有字符串;
解题思路
这道题最复杂的地方在于同一个字符可能被两个重复字符串占用,比如aaaaaabcabcabca,其中重复字符串有6[a],3[bcd],或5[a]3[abc],第六个6决定了到底算是哪一个字符串中的决定了不同的编码方法,像这种不同选择导致不同的结果,一般来说有深搜和动态规划两种思路。
首先从深搜的角度考虑,先找到一段重复字符串比如abcabcabc,然后递归的去寻找其前面的字符串和后面的字符串的最小编码,合并起来就是这一种以abcabcabc进行分割得到的结果,得到所有可能的结果,然后比较得出长度最小的那一种。那么问题分解到如何找到第一步的重复字符串,即找到abcabcabc这个子串,这里使用一种定向的方法,设子串的长度最大增长到length/2,依次从第一个位置开始向下开始寻找是否存在该子串的重复子串,每找到一个子串,就将其计数变量加1。这种深搜的思路只能通过80%的测试用例。
从动态规划的角度来看,dp[i][j]表示从i到j的最短字符编码,dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j])(i<=k<=j),主要注意的是,这样的方式将str[i:j]分成两部分进行编码,然后合并,但这个合并只是简单的连接,并没有考虑到这两部分可以进一步编码这个情况,比如对于aaaaaa,分为a,5[a],连接之后就是a5[a],但其实还是可以进一步合并为6[a],也就是说子串str[i:j]还有一种情况就是整个子串可以进一步编码为k[base],那么就要寻找str[i:j]中的base,以及对应的k。比如ababab,base=ab,k=3,寻找的方式是令子串t,t+t拼接得到一个新串s,pos=s.find(t,1),如果pos>t.size(),那么说明这个子串无法编码k[]形式,否则,k=t.size()/pos,解释如下:
实现代码
代码一:
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
string to_string(int num){
stringstream sss;
sss<<num;
string str;
sss>>str;
return str;
}
string encoding(string& str){
int len=str.length();
if(len<=4) return str;
vector<vector<string> >dp(len,vector<string>(len,""));
for(int step=1;step<=len;step++){
for(int j=0;j+step-1<len;j++){
int k=j+step-1;
dp[j][k]=str.substr(j,step);
string t=dp[j][k];
for(int m=j;m<k;m++)
if(dp[j][k].length()>(dp[j][m]+dp[m+1][k]).length())
dp[j][k]=dp[j][m]+dp[m+1][k];
string s=t+t;
int pos=s.find(t,1);
if(pos<t.length()){
string replace="";
replace=to_string(t.length()/pos)+"["+dp[j][j+pos-1]+"]";
if(replace.length()<dp[j][k].length())
dp[j][k]=replace;
}
}
}
return dp[0][len-1];
}
int main(){
string str;
getline(cin,str);
string res=encoding(str);
cout<<res.length()<<endl;
return 0;
}