最小替换子串长度问题
问题描述
小F得到了一个特殊的字符串,这个字符串只包含字符A、S、D、F,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得A、S、D、F这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。
解题思路
本题中我们需要修改一个子串使得原字符串满足。
由于题目中对我们修改子串中的字母的数量没有限制,我们不必考虑去替换子串中的哪些字符,也因此,此时我们只需要考虑子串之外的字母的个数即可。但是子串的位置,长度也一直在变换,我们需要选择去统计子串中字符的个数,或者子串外剩余字符的个数。我们用string_list[0,1,2,3]分别表示A,S,D,F的个数,用substring_list[0,1,2,3]分别表示子串中A,S,D,F的个数。
此处可以有两种思路:
- 当A、S、D、F的字符数比较接近的时候,那么最后计算得到的最小子串的长度
sum(substring_list)一定会远小于原字符串的长度sum(string_list)的一半。此时我们统计子串内各字符的个数substring_list,然后用string_list分别减去,这种方法的效率最高 - 当A、S、D、F的字符数相差很大的时候,那么
sum(subString_list)的大小将会比较接近sum(string_list),很显然,此时统计子串之外的字符的个数的效率会更高
接着我们讨论如何获取子串,很显然的一点是,最小的子串的长度一定和得到的这个子串的位置有关。我们可以研究一个极端情况:当子串就是原字符串时,很显然,此时很容易调整子串的内容使得,而且很显然,子串的数量会很多很多。如果我们遍历所有的情况,那么性能将会很低,我们应该避免这种情况。
那么我们获取子串的方法就应该是优先讨论小子串,且在找到时就退出,这时候一定找到的就是子串最短的情况。
实现这种想法很简单,我们使用两层循环,第一层循环的自增值是子串的长度,第二层循环的自增值是子串的起始位置。伪代码如下。
for i in range(1, len(input)):
for j in range(len(input)+1-i):
if find_answer(input[i:i+j], input):
return i
由于原字符串一定是符合答案要求条件的子串,所以该循环一定可以找到答案。
最后我们讨论如何才能编写这个find_answer函数。由于我们最终需要得到的字符串满足,所以我们需要的子串长度仅与字串外的数量最多的字符的数量有关,即我们需要将子串中剩余三个字符的数量变换的和这个最长的字符相同,如果还有剩余,平均分配即可。
也因此,我们只需计算一个判断式就可以判断子串是否符合条件
other_string_list = [0]*4
for i in range(4):
other_string_list[i] = string_list[i]-subString_list[i]
other_string_list.sort(reverse=True)
return other_string_list[0]*4-sum(other_string_list)
至此,我们就可以得到实现题目条件的最小子串长度。