最短字符编码

题目描述

 给定一个非空字符串, 按照如下方式编码, 使得编码后长度最小, 返回编码后的长度: 编码规则为: 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]’;
  3. 输入字符串为全小写英文字母, 长度<=160;
  4. 如果编码后长度没有更小, 则保留原有字符串;

解题思路

  这道题最复杂的地方在于同一个字符可能被两个重复字符串占用,比如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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值