[OJ]平均串问题,存在超时问题未解决

众所周知,两个数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)2626
26:表示从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"
  • 17
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值