众所周知,两个数a和b的平均数计算公式为(a+b)/2。
实际上平均数也可以描述为:从较小的数依次遍历到较大的数,将遍历的数放入一个列表中,该列表的中心元素。例如:求
3和7的平均数,列表为{3,4,5,6,7},平均数即为中心元素5。
类似的可以定义两个字符串A和B的平均串:设A的字典序小于B,,将由A按字典序生成B依次产生的字符串放入一个集合中,
该集合的中心元素即为平均串。例如求AZ和BF的平均串,按字典序生成的集合为{AZ,BA,BB,BC,BD,BE,BF},取中心元素BC即
为平均串。
给定两个长度相同,内容均为大写字母的字符串s1和s2,s1的字典序小于s2,输出它们的平均串。
1.暴力解决方法:使用大量递归,并占用大量内存,罗列每一个数,空间复杂度和时间复杂度极高
import sys
sys.setrecursionlimit(100000000) #例如这里设置为十万
def main():
#code here
global s1,s2,s
s1=input().strip()
s2=input().strip()
s=s1
if s1.isupper() and s2.isupper() and len(s1)==len(s2) and (len(s1)>=1 and len(s1)<=200000):
global newStr,count
count=1
newStr=[]
newStr.append(s1)
meanString(len(s1)-1)
if count%2!=0:
global meanStr
meanStr=count//2+1
#subStr=newStr[1015:]
addString(len(s1)-1)
print(s)
pass
#在s1的基础上,加上count//2
def addString(k):
global meanStr,s
for i in range(k,-1,-1):
while meanStr>1 and s<s2:
if i==len(s)-1 and ord(s[i])!=90:
meanStr-=1
s=s[:i]+s[i].replace(s[i],chr(ord(s[i])+1))+s[i+1:]
else:
if ord(s[i])!=90:
s=s[:i]+s[i].replace(s[i],chr(ord(s[i])+1))+s[i+1:]
i=len(s)-1
s=s[:i]+s[i].replace(s[i],chr(ord(s[i])-25))+s[i+1:]
meanStr-=1
addString(i)
elif ord(s[i])==90:
addString(i-1)
#通过Ascii码进行比较 A=65 Z=90
def meanString(k):
global s1,s2,newStr,count
#通过首字母,保证s1的字典序小于s2
#生成奇数个字符串
if s1<s2:#回溯
for i in range(k,-1,-1):#从后往前循环
#针对最后一个数
while s1<s2:
if i==len(s1)-1:
if ord(s1[i])<90:
#将最后一个字符串替换
s1=s1[:i]+s1[i].replace(s1[i],chr(ord(s1[i])+1))+s1[i+1:]
count+=1
newStr.append(s1)
elif ord(s1[i])==90:
i-=1
meanString(i)
break
elif i!=0:
if ord(s1[i])<90:
s1=s1[:i]+s1[i].replace(s1[i],chr(ord(s1[i])+1))+s1[i+1:]
i=i+1
s1=s1[:i]+s1[i:].replace(s1[i],chr(ord(s1[i])-25))
i=len(s1)-1
count+=1
newStr.append(s1)
meanString(i)
break
elif ord(s1[i])==90:
i-=1
meanString(i)
break
elif i==0:
if ord(s1[i])<90:
s1=s1[:i]+s1[i].replace(s1[i],chr(ord(s1[i])+1))+s1[i+1:]
i=i+1
s1=s1[:i]+s1[i:].replace(s1[i],chr(ord(s1[i])-25))
i=len(s1)-1
count+=1
newStr.append(s1)
meanString(i)
break
elif ord(s1[i])==90:
break
if __name__ == '__main__':
main();
运行结果如下:
问题:当字符长度为4时,进行上千次递归,程序出现问题,闪退
2.规律总结方法:
‘’’
规律总结:
AZ–DF : 1+52+6=59 (count=59)
(Z-Z+1):表示最后一位数,+1表示加上最开始的AZ,这里为1
(D-A-1)*26:表示BA–BZ…CA–CZ,,这里为52
(F-A)+1:表示从DA–DF之间的数,+1表示加上最后的DF这里为6
ABF–DDH:21+624+1352+78+7=2083(count=2083)
(Z-F+1):表示最后一位数ABF–ABZ,+1表示最开始的ABF,这里为21
(Z-B)*26:表示ACA–ACZ…AZA–AZZ,这里为624
(D-A-1)2626:表示BAA–BZZ…CAA–CZZ,这里为1352
(D-A)*26:表示从DAA–DAZ…DCA–DCZ,这里为78
(H-A+1):表示DDA–DDH,+1表示加上最后的DDH,这里为7
AABF–DDBH:21+624+16900+35152+2028+26+8=54759(count=54759)
(Z-F+1):表示最后一位数AABF–AABZ,+1表示最开始的AABF,这里为21
(Z-B)26:表示AACA–AACZ…AAZA–AAZZ,这里为624
(Z-A)2626:表示ABAA–ABZZ…AZAA–AZZZ,这里是16900
(D-A-1)262626:表示从BAAA–BZZZ…CAAA–CZZZ,这里是35152
(D-A)2626:表示从DAAA–DAZZZ…DCAAA–DCZZZ,这里是2028
(B-A)*26:表示从DDAA–DDAZ,这里是26
(H-A)+1:表示从DDBA–DDBH,+1表示加上最后的DDBH,这里是8
规律总结: 如果存在长度在2位以上的数,且首位不相等,首位相减大于1(这里D-A),那么count/2,即中心点一定在首位的式子中
例如AABF–DDBH:的中心点,在BAAA到DZZZ之间
如果存在长度在2位以上的数,且首位不相等,首位相减等于1(这里B-A),那么count/2,中心点一定落在第二位的式子中
AZZZ–BBZZ的中心点,在BAAA到BBZZ之间
如果存在长度为2位,且首位不相等,首位相减等于1(这里B-A),那么count/2,中心点一定落在第二位的式子中
AZ–BF的中心点在BA到BF之间
‘’’
'''
规律总结:
AZ--DF : 1+52+6=59 (count=59)
(Z-Z+1):表示最后一位数,+1表示加上最开始的AZ,这里为1
(D-A-1)*26:表示BA--BZ...CA--CZ,,这里为52
(F-A)+1:表示从DA--DF之间的数,+1表示加上最后的DF这里为6
ABF--DDH:21+624+1352+78+7=2083(count=2083)
(Z-F+1):表示最后一位数ABF--ABZ,+1表示最开始的ABF,这里为21
(Z-B)*26:表示ACA--ACZ...AZA--AZZ,这里为624
(D-A-1)*26*26:表示BAA--BZZ..CAA--CZZ,这里为1352
(D-A)*26:表示从DAA--DAZ...DCA--DCZ,这里为78
(H-A+1):表示DDA--DDH,+1表示加上最后的DDH,这里为7
AABF--DDBH:21+624+16900+35152+2028+26+8=54759(count=54759)
(Z-F+1):表示最后一位数AABF--AABZ,+1表示最开始的AABF,这里为21
(Z-B)*26:表示AACA--AACZ...AAZA--AAZZ,这里为624
(Z-A)*26*26:表示ABAA--ABZZ...AZAA--AZZZ,这里是16900
(D-A-1)*26*26*26:表示从BAAA--BZZZ...CAAA--CZZZ,这里是35152
(D-A)*26*26:表示从DAAA--DAZZZ...DCAAA--DCZZZ,这里是2028
(B-A)*26:表示从DDAA--DDAZ,这里是26
(H-A)+1:表示从DDBA--DDBH,+1表示加上最后的DDBH,这里是8
规律总结:
如果存在长度在2位以上的数,且首位不相等,首位相减大于1(这里D-A),那么count/2,即中心点一定在首位的式子中
例如AABF--DDBH:的中心点,在BAAA到DZZZ之间
如果存在长度在2位以上的数,且首位不相等,首位相减等于1(这里B-A),那么count/2,中心点一定落在第二位的式子中
AZZZ--BBZZ的中心点,在BAAA到BBZZ之间
如果存在长度为2位,且首位不相等,首位相减等于1(这里B-A),那么count/2,中心点一定落在第二位的式子中
AZ--BF的中心点在BA到BF之间
'''
def main():
#code here
global s1,s2
s1=input().strip()
s2=input().strip()
if s1.isupper() and s2.isupper() and len(s1)==len(s2) and (len(s1)>=1 and len(s1)<=200000):
global newStr,count,current_count,current_str,end_count
count=0
current_count=0
end_count=0
current_str=s1
substractString(0,s1,s2)
if count%2!=0:
#print(count)
global meanStr
#在cureent_count的基础上进行计数
#即在current_str的基础上进行加法A
meanStr=count//2-current_count
addString(len(s1)-1)
print(current_str)
pass
#在s1的基础上,加上count//2
def addString(k):
global current_str,meanStr
#从末尾开始加,每当Z时,前一位变为原来的数加1,末尾置为A,例如BAAA
for i in range(k,-1,-1):
'''
判断meanStr落在哪个范围内
如果1<=meanStr<=26,则在BAAA到BAAZ之间
如果26+1<=meanStr<=26*26,则在BABA到BAZZ之间
如果26*26+1<=meanStr<=26*26*26,则在BBAA到BZZZ之间
'''
if meanStr>0:
if i==len(s1)-1:
if pow(26,len(s1)-(i+1))<= meanStr<=pow(26,len(s1)-i):
current_str=current_str[:i]+current_str[i].replace(current_str[i],chr(ord(current_str[i])+meanStr))+current_str[i+1:]
break
elif i!=0:
while pow(26,len(s1)-(i+1))+1<= meanStr<=pow(26,len(s1)-i):
meanStr-=(pow(26,len(s1)-(i+1)))
current_str=current_str[:i]+current_str[i].replace(current_str[i],chr(ord(current_str[i])+1))+current_str[i+1:]
if meanStr<pow(26,len(s1)-(i+1))+1:
addString(i+1)
break
else:#当i==0时
while pow(26,len(s1)-(i+1))+1<= meanStr<=end_count:
meanStr-=(pow(26,len(s1)-(i+1))+1)
current_str=current_str[:i]+current_str[i].replace(current_str[i],chr(ord(current_str[i])+1))+current_str[i+1:]
if meanStr<pow(26,len(s1)-(i+1))+1:
addString(i+1)
break
pass
#需要记录首位的状态和此时计数的状态
def substractString(k,s1,s2):
global count,current_count,current_str,end_count
for i in range(k,len(s1)):#s1中从前往后遍历
if i==0:
if ord(s2[i])>ord(s1[i]):
if ord(s2[i])-ord(s1[i])>1:
for j in range(len(s1)-1,-1,-1):#从后往前遍历
if j!=i:
if j==len(s1)-1:
count+=ord('Z')-ord(s1[j])+1#开始时的末尾
else:
count+=(ord('Z')-ord(s1[j]))*pow(26,len(s1)-1-j)
current_count=count
current_str=chr(ord(s1[i])+1)+(len(s1)-1)*'A'#首字母加1,除了首字母外都是A
count+=(ord(s2[i])-ord(s1[i])-1)*pow(26,len(s1)-1)#计算首位
#首位结束时的count
end_count=count
elif ord(s2[i])-ord(s1[i])==1:#当首位相差等于1时且2位数以上
if len(s1)==2:
for j in range(len(s1)-1,-1,-1):#从后往前遍历
if j!=i:
if j==len(s1)-1:
count+=ord('Z')-ord(s1[j])+1#开始时的末尾
else:
count+=(ord('Z')-ord(s1[j]))*pow(26,len(s1)-1-j)
current_count=count
current_str=chr(ord(s1[i])+1)+(len(s1)-1)*'A'#首字母加1,除了首字母外都是A
count+=(ord(s2[i])-ord(s1[i])-1)*pow(26,len(s1)-1)#计算首位
#首位结束时的count
end_count=count
elif len(s1)>2:
for j in range(len(s1)-1,-1,-1):#从后往前遍历
if j!=i:
if j==len(s1)-1:
count+=ord('Z')-ord(s1[j])+1#开始时的末尾
elif j==1:
current_count=count
count+=(ord('Z')-ord(s1[j]))*pow(26,len(s1)-1-j)
if ord(s1[i+j])+1<90:
current_str=s1[i]+chr(ord(s1[i+j])+1)+(len(s1)-1-j)*'A'#第二个字母加1,除了首字母、第二个字母外都是A
#首位结束时的count
end_count=count
count+=(ord(s2[i+j])-ord(s1[i+j])-1)*pow(26,len(s1)-1-j)#计算首位
else:
current_str=chr(ord(s1[i])+1)+(len(s1)-1)*'A'
substractString(i+1,s1,s2)
break
else:
count+=(ord('Z')-ord(s1[j]))*pow(26,len(s1)-1-j)
break
elif ord(s1[i])==ord(s2[i]):
substractString(i+1,s1,s2)
break
else:
break
elif i!=len(s1)-1:
count+=(ord(s2[i])-ord('A'))*pow(26,len(s1)-1-i)
else:#结束时的末尾
count+=ord(s2[i])-ord('A')+1
if end_count==0:
end_count=count
pass
if __name__ == '__main__':
main();
结果运行如下:直接突破50位长度,最终多少长度这里没有进行计算,但能够满足许多需求
存在超时问题,超出该题的时间限制2m,未解决该问题,或许可以改用C++代码,能够大幅降低时间复杂度
3.为了找到两个字符串的平均串,我们可以按照字典序生成从第一个字符串到第二个字符串的所有中间字符串,然后找到中间位置的字符串作为平均串。下面是具体的步骤和代码实现:
步骤:
- 确保字符串s1的字典序小于s2。如果不是,则交换两个字符串以确保条件满足。
- 初始化一个空集合,用于存储按字典序遍历的所有字符串。
- 从s1开始,逐步修改每个字符直到变成s2,同时将这些字符串添加到集合中。具体来说,对于s1和s2的每个位置i,从s1[i]开始逐步增加到s2[i],并构建对应的字符串添加到集合中。这样可以保证生成的字符串是字典有序的。
- 找到集合中的字符串数量N(实际上这将是中间位置之前的字符串数量加上中间位置之后的字符串数量)。由于集合中的字符串是按字典序排序的,所以中间位置的字符串将是平均串。如果集合中的元素数量是奇数,则中间位置就是 (N+1)/2;如果集合中的元素数量是偶数,则中间位置介于 N/2 和 (N+1)/2 之间(即取两个中间位置的平均值)。计算平均串的方式可以是简单地取这两个中间位置的字符串的对应字符的平均值(这实际上不太可能产生有意义的平均串,因为我们处理的是字母而非数字),或者可以通过其他方法确定更合适的平均串。由于这个问题没有明确规定如何平均字母,我们可以假设需要找到中间位置的字符串。因此,我们需要向下取整来确定真正的中间位置。
- 返回集合中对应中间位置的字符串作为平均串。假设返回的是更接近于末尾的中间位置的字符串(即向下取整的中间位置)。
def average_string(s1, s2):
# 确保s1字典序小于s2
if s1 > s2:
s1, s2 = s2, s1
# 构建按字典序排列的字符串集合
strings_set = set()
for i in range(len(s1)):
# 逐步生成从s1到s2的所有可能字符串并添加到集合中
for c in range(ord(s1[i]), ord(s2[i]) + 1): # 使用ord函数获取字符的ASCII值范围遍历字符集
temp_str = list(s1) # 创建s1的副本以避免修改原始字符串
temp_str[i] = chr(c) # 修改副本中的字符以构建新的字符串
strings_set.add(''.join(temp_str)) # 将新构建的字符串添加到集合中
# 计算中间位置的索引并返回对应的字符串作为平均串
middle_index = len(strings_set) // 2 # 计算中间位置索引(向下取整)
average_str = list(strings_set)[middle_index] # 获取中间位置的字符串(转换为列表方便索引)或者直接用二分查找等优化手段获取中间元素的值也可以避免将整个集合转换为列表带来的性能损耗。这里为了简化代码直接使用了列表访问方式。
return average_str
# 测试函数
print(average_string("AZ", "BF")) # 输出应为 "BC"