BASIC-19 基础练习 完美的代价

转载:https://blog.csdn.net/hallohalloha/article/details/113332805

题目描述

回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。 

交换的定义是:交换两个相邻的字符 

例如mamad 
第一次交换  ad  :  mamda 
第二次交换  md  :  madma 
第三次交换  ma  :  madam  (回文!完美!) 

输入

第一行是一个整数N,表示接下来的字符串的长度(N  < =  8000) 

第二行是一个字符串,长度为N.只包含小写字母 

输出

如果可能,输出最少的交换次数。 

否则输出Impossible 

样例输入

5 
mamad 

样例输出

3

解决:贪心构造字符串两端相同

问题分析:

关键是要找到贪心策略。对于这一题来说,首先判断这个字符串能不能够经过交换变成完美的回文字符串,满足条件后每一步都使用贪心策略。

①判断:
若字符串长度为偶数,则每个字符出现的次数都必须是偶数次,否则不对称;
若字符串长度为奇数,则只能有一个字符出现奇数次(在字符串最中间出现一次);
判断实现比较简单,注意减少代码复杂度(题给字符串长度范围是8000,python很容易运行超时)。
②贪心策略:
对于偶数长度的字符串,我们从第一个开始遍历,再倒序遍历出同样的,这个倒序遍历出来的序号,就是该移动的步数。 倒序列表需要不断更新,已经构成回文的外层字符不再考虑。
对于奇数的字符串,其实贪心策略和偶数的时候一样,只不过我们一直遍历下去会有一个字符没有匹配,那么这个字符肯定是放在中间的,我们设置一个判断,假如剩余的该字符个数不是1,按照和偶数一样的遍历,如果该字符是1,直接移动到最中间的位置。

图解:

省时策略:

python中的迭代和交换都要花费大量的时间。

在改进的代码中,对于找到的字符,我不对它进行逐次的交换,而是直接pop掉,利用它的索引找到需要移动的次数。

下一次的遍历针对的即是已经去掉外层字符的新列表,这样迭代下去,列表会越来越短,就达到了省时的目的。

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掉
            break

        elif k == 1:  # 如果找不到相同的,该字符放到中间
            pal.pop(0)  #找不到的这个数丢弃,不能影响后续判断
            # impossible的两种情况:
            # ①偶数个字符,却存在一个字符出现奇数次
            # ②奇数个字符,但已经有一个字符只出现奇数次了,不能再出现下一个
            if n % 2 == 0 or flag == 1:
                print('Impossible')
                exit()
            flag = 1
            count += int(n/2) - i #把出现奇数次的字符直接移到中间需要的步数
print(count)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值