Topcoder Open 2014 Algorithm Round 1A

本文深入探讨了字符串变换算法,通过详细分析和优化,提出了两种高效的求解最小字典序的方法。首先介绍了朴素解法,然后通过进一步分析发现了优化策略,将算法复杂度降低至O(|S|*log(|S|))。此外,文章还延伸至更复杂的问题场景,阐述了解决字典序最小变换问题的贪心策略及其实现细节。

250pt:

题意:给定一个变换规则:对一个长度为字符串S,任意选定一个序号i (0<= i <= length(S) - L),可对该字符串进行以下变换:

①保留前i个字符

②对接下来L个字符进行字典序排序

③移除剩下的字符

输入一个string S和int L,求S进行任意次变换能得到的最小字典序的结果。

范围:

S包括2~50个字符,只包含大小字符('A'~'Z')

L为[2, length(S)]

分析:

当S为"TOPCODER",L=3;最简单的的变换步骤为

①"TOPCODER"=> i = 3 => "TOPCDO"

②"TOPCDO"    => i = 2 => "TOCDP"

②"TOCDP"       => i = 1 => "TCDO"

②"TCDO"         => i = 0 => "CDT"

但第一次选择从i=3的原因不是那么显而易见,让我们再看下面一组例子的分析

当S为"ABRACADABRA", L=5;最朴素的变换步骤为

①"ABRACADABRA"=>  i = 6 =>"ABRACAAABDR"

"ABRACAAABDR"=>  i = 5 =>"ABRACAAABD"

③"ABRACAAABD"=>  i = 4 =>"ABRAAAABC"

④"ABRAAAABC"     =>  i = 3 =>"ABRAAAAB"

⑤"ABRAAAAB"=>  i = 2 =>"ABAAAAR"

⑥"ABAAAAR"=>  i = 1 =>"AAAAAB"

⑦"AAAAAB"=>  i = 0 =>"AAAAA"

朴素解法:

每次对L个字符进行排序,总共要进行length(S)-L+1次;

#include <algorithm> 
#include <string>
using namespace std;
class EllysSortingTrimmer
{
public:
	string getMin(string S, int L)
	{
		for (int i = S.size()-L; i >=0 ; --i)
		{
			sort(S.begin()+i,S.begin()+i+L);
		}
		return S.substr(0,L);
	}
};
算法复杂度为O(|S|*L*log(L));

进一步分析:

是否需要进行这么多次排序?

如果直接对S进行排序是否可以?不可以,"TOPCODER"的例子就是一个反例。

但从朴素解法中i=0的数据可发现,其被排序的L个字符的组成为原S字符串的第一个字符+原字符串S(1,end)中按字典序排列的前L-1个字符

因此只需要两次排序即可得到最后结果:

①对除了第一个字符的S(1,end)进行排序,排列好其前L-1小的个字符

②再将第一个字符插入到步骤①中得到的前L-1个已序字符的相应位置。

简单的实现方式如下:

#include <algorithm> 
#include <string>
using namespace std;
class EllysSortingTrimmer
{
public:
	string getMin(string S, int L)
	{
		partial_sort(S.begin()+1,S.begin()+L,S.end());
		sort(S.begin(),S.begin()+L);
		return S.substr(0,L);
	}
};

算法复杂度为O(|S|*log(|S|);


500pt:

题意:给定一个变换规则:对一个长度为字符串letters,最大距离为maxDistance,可对该字符串进行变换生成新的字符串S,新生产的字符串S中的每一个字符的index与原字符串letters相应的字符的index相差不超过(小于等于)maxDistance。

输入一个string letters和一个最大距离int maxDistance,求变换后字典序最小的结果。

范围:

letters包含1~50个字符,只是'A'~'Z'

maxDistance~[1,9]

分析:

要求字典序最小,想到用贪心,对于新字符串S上index位上的选择,

①如果原始位中index-maxDistance位字符还没有被使用,那么就选择,不然的话后来就没有机会给他用了,因为后面再用到的话,其距离就会大于maxDistance

②否则的话,选从index-maxDistance~index+maxDistance中未使用过的最小字典序的一个,如果有相同的话,选择序号最小的

代码实现如下:

#include <string>
#include <vector>
#include <cmath>
using namespace std;
class EllysScrabble
{
public:
	string getMin(string letters, int maxDistance)
	{
		vector<bool> flag(letters.size(), false);
		string ret;
		for (int index = 0; index < (int)letters.size(); ++index)
		{
			if (index-maxDistance >= 0 && !flag[index-maxDistance])
			{
				ret.push_back(letters[index-maxDistance]);
				flag[index-maxDistance] = true;
			}
			else
			{
				char minChar = 'Z' + 1;
				int pos = -1;
				for (int i = max(0, index-maxDistance); i <= min((int)letters.size()-1, index+maxDistance); ++i)
				{
					if (!flag[i] && minChar > letters[i])
					{
						minChar = letters[i];
						pos = i;
					}
				}
				ret.push_back(minChar);
				flag[pos] = true;
			}
		}
		return ret;
	}
};
算法复杂度为O(N*maxDistance);


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值