• 试题 基础练习 完美的代价


     一.题目

    题目链接
      http://lx.lanqiao.cn/problem.page?gpid=T60
    问题描述
      回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
      交换的定义是:交换两个相邻的字符
      例如mamad
      第一次交换 ad : mamda
      第二次交换 md : madma
      第三次交换 ma : madam (回文!完美!)
    输入格式
      第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
      第二行是一个字符串,长度为N.只包含小写字母
    输出格式
      如果可能,输出最少的交换次数。
      否则输出Impossible
    样例输入
    5
    mamad
    样例输出
    3

    二.解决

    思路:交换的定义是交换两个相邻的字符!最后交换成功的结果是两边字母对称。

    问题:什么字符串一定不可能成为回文串?

    字符串有大于等于2种的字母个数为奇数    也即:回文串只能最多有一种字母其个数为奇数,其余都应为偶数

    以下是建立在该字符串一定为回文串的处理方法

    从前向后遍历字符串,对每一个字符找到与该字符相同字符的位置,因为要使得移动次数最少,所以可以从后往前遍历字符串,找到第一个与它相等的字符,并且移动到对称的位置,统计移动的次数。

    从左边往右边走  i = 0 to “对称位置”    i初始位置为0,对称位置为最右边,也即n-1      i++

    从右边往左边走  j = “对称位置” to i+1  找到第一个与a[i]值(字母)相同的j   然后去移动  使得i的对称位置的字母为a[j]  总移动次数+=这里移动的次数 “对称位置”也往左移动一位(“对称位置”--

    最后,我们要考虑一个问题,也即如果遇到个数只有一个的字母时我们应该要做的事情,我们要确保它在中间位置,个数只有一个的字母判断条件:当遍历完右边后都没有字母与它相等,这时j==i(也即遍历到了自身),我们需要将这个字母移动到中间,只需将总移动次数+=(N/2-i),不需要真正将它移动,因为在后面的寻找对称字母过程中,可能会将“这中间字母”又移动到其他地方,白做功夫,所以我们暂时先不移动它,等到所有的寻找过程完成之后,再对它进行移动(当然我们其实并不需要真的去移动它,因为我们已经知道移动次数:中间位置N/2-当前位置i)。

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 8005
    int mp[30];
    int main(){
        int N,sum=0,k,i,j,flag=0,flag1=0;
        string s;
        cin>>N;
        cin>>s;
        memset(mp,0,sizeof(mp));
        //判断字符串能否成为回文串 
        for(int i=0;i<N;i++){
            mp[s[i]-'a']++;
        } 
        for(int i=0;i<24;i++){
            if(mp[i]%2==1)    flag1++;
            if(flag1>1)    break;
        } 
        if(flag1>1) printf("Impossible
    ");
        else{
        int temp=N-1;
        for(i=0;i<temp;i++){//从前向后找 
            for(j=temp;j>i;j--){//从后向前找 
                if(s[i]==s[j]){//遇到相等的字母则需要交换到合适的地方
                 //从j位置移动到与i对称的位置(即temp)
                 for(k=j;k<temp;k++){
                     s[k]=s[k+1];
                 } 
                s[temp]=s[i];//也即之前的s[j],但s[j]已经被其他字母占了,所以用与它值相等的s[i]表示 
                sum+=temp-j;//这次移动的次数
                temp--;//缩小边界
                break; 
                }
            }
            if(j==i){//也即之前的“从后向前找”找不到相等的字母,只有自身,则将它移动到中间 
                sum+=(N/2 - i);
            }
        }
        cout<<sum<<endl;
        }
    }
  • 相关阅读:
    Android4.0 以后不允许在主线程进行网络连接
    关于升级linux下apache
    事物复制中大项目(Large Article)出问题如何快速修复
    国企银行面试总结
    Git命令详解(一)-个人使用
    函数体的规模要小,尽量控制在 50 行代码之内
    函数的功能要单一,不要设计多用途的函数
    在函数体的“出口处”,对 return 语句的正确性和效率进行检查
    在函数体的“入口处”,对参数的有效性进行检查
    有些场合用“引用传递”替换“值传 递”可以提高效率
  • 原文地址:https://www.cnblogs.com/Aiahtwo/p/12738229.html
Copyright © 2020-2023  润新知