蓝桥杯基础练习VIP-完美的代价
对大佬的代码分析扒出来更详细的思路
https://blog.csdn.net/hallohalloha/article/details/113332805
n = int(input())
pal = list(input())
count = 0 #count用来计交换的次数
flag = 0 #flag判断是否已经有一个单独的奇个数的字符了
i=0 #用于计数外层丢弃的字符(单侧,比如左侧,用来最后计算单个奇数字符移动到中间的次数)
while(len(pal)>1):
for k in range(len(pal)-1,0,-1): # 从后面往前一直到索引1,寻找和pal[0]相同的pal[k]
if pal[k] == pal[0]:
#print(k)
count += len(pal) - k - 1 # 计数器加 len(pal)-k-1这里是直接考虑把逆序遇到的一个字符放到右侧最外面,找到移动次数和索引的规律是关键!注意是把计算和移动分开来想,而不是真正的去交换移动!
#print(count)
#最外层的已构成回文,不考虑了
pal.pop(0)#注:pal(i)被pop掉后,索引整体前移1,所以k的位置需要-1
i+=1#记录扔掉了几层
pal.pop(k-1)#把找到的字符直接移到最外层,即直接pop掉,因为前移了所以这里的k-1就是刚才的k
break#这里的break很重要,比如最后剩到 aa,此时k==1,但是是找到了相同的;不能进入下面代码段!!
elif k == 1: # 逆序到找不到相同的,该字符只能放到中间,这个字符肯定是出现奇数次了,这是前提!
pal.pop(0) #找不到的这个数丢弃,不能影响后续判断,把移动和计算分开
# impossible的两种情况:这里需要手写找出规律!重要
# ①偶数个字符,却存在一个字符出现奇数次
# ②奇数个字符,但已经有一个字符只出现奇数次了,不能再出现下一个,用flag==1 标志已经有一个字符出现了奇数次
if n % 2 == 0 or flag == 1:
print('Impossible')
exit()
flag = 1
count += int(n/2) - i #把出现奇数次的字符直接移到中间需要的步数;注意这里的n是原先长度,n/2 是原先一半长,需要减去扔掉的长度才是距离中间的长度,也即交换次数(这样也是一个贪心的思想,不考虑其他交换,一直把这个换到中间去,肯定是最少步骤)
print(count)
反思:
- 外围的字符构成回文数就直接扔掉,没有必要考虑,简化问题规模
- 贪心的体现就是目标就是一个,把当前字符一直移动到位置上,管其他的,这样肯定最小