zoukankan      html  css  js  c++  java
  • 基础练习 完美的代价

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

    图1,假设初始串中,左侧A的对称位置是字母B。先将右侧的A移到右侧B的位置,再移动两个B到对称位置,与先移动左侧B到左侧A的位置,再移动两个A到对称位置,移动次数是一样的。首先,后移动的一对字母在移动过程中,不会与前一对已到位的字母进行交换。这是因为,移动后一对字母时,只能按单个方向移动,如果在移动过程中与前一对字母发生交换,那么相当于前一对字母向相反方向进行了移动,而它们原来位置是对称的,这样移动后就不可能还对称了。因此,可认为两对字母的移动是独立的,总的移动次数是两对字母的移动次数之和,而每一对字母的移动次数与可能的位置无关,因此先移动哪一个字母并没有关系。

           (图1)

          为了保证后移动的字母不会与前面已经移动过的字母发生交换,我们可以从原始字符串的最左边开始,依次选择各个字母为当前移动的那对字母,而且就以该字母的位置为最终的对称位置,而这对字母的另一个,当然就选取最右侧对称位置开始向左第一个出现的相同字母。如果字符串的长度为偶数,则所有字母出现次数都是偶数,以上过程可一直进行下去,在移动倒数第二对字母到位后,最后一对字母也同时到位,得到结果的回文串。如果字符串的长度为奇数,则有可能在这一过程中提前碰到最终应该放在串中间的那个字母。根据前面的分析,不能马上将它放到串的中间,因为并不能保证它不会与其后的字母发生交换。这时我们可以转而考虑右边对称位置的字母,将它作为当前考虑的那一对,在左侧当前位置向右查找第一个相同字母,进行移动。

          以上的算法已经基本清晰了,还差一点小细节,我们怎么知道当前位置的字母就是那个最终应该放在串中间的那个字母呢?首先这个字母必然是那个唯一出现次数为奇数的字母,我们可以在判断原串是否可以变成回文串时顺便记录下来。其次,由于这个字母可以在原串出现若干次,所以并不是第一次碰到的该字母就一定是最终移动到串中间的那个字母。假设该字母共出现n次,则应该是第(n+1)/2次碰到的那个字母最终放在串中间。这一过程需要记录该字母已出现过几次,我们可以变通一下对该字母的处理,方法如下:只要碰到该字母,就以对称位置的字母为当前要移动的那对字母。实际上,若对称位置也是该字母,那么正好已到位。

          根据以上分析得到的算法,我们能够以最少的移动次数,也就是最少的交换次数,将原字符串变为一个回文串,只要累加每一对字母所移动的次数就得到原题目要求的答案。 注意本题并不是是要得到回文串,仅仅只是统计次数而已,二各个字母移动的次数互相独立。  

     1 #include<stdio.h>
     2 
     3 int change(char str[],char x,int len){
     4     int i,j,k,count = 0;
     5     for(int i = 0;i < len / 2;i++){
     6         if(str[i] == x){
     7             for(j = i;j < len - i - 1;j++){
     8                 if(str[j] == str[len - i - 1]) break;
     9             }    
    10             count += j - i;
    11             for(k = j;k > i;k--)  str[k] = str[k-1];
    12             str[i] = str[len - 1 - i];
    13         }
    14         else{
    15             for(j = len - i - 1;j >= i;j--){
    16                 if(str[i] == str[j]) break;
    17             }
    18             count += len - 1 - i - j;//i,j对称,则i+j=len-1; 
    19             for(k = j;k < len - 1 - i;k++) str[k] = str[k+1];
    20             str[len - i - 1] = str[i];
    21         }
    22     }
    23     return count;
    24 }
    25 
    26 int main(){
    27     char str[8000] = {0},x;
    28     int len,b[26] = {0},i,j,k = 0;
    29     scanf("%d",&len);
    30     getchar();
    31     for(i = 0;i < len;i++){
    32         scanf("%c",&str[i]);
    33     }
    34     for(i = 0;i < len;i++){
    35         j = str[i] - 'a';
    36         b[j]++;
    37     }
    38     for(j = 0;j < 26;j++){
    39        if(b[j] % 2 != 0){
    40              k++;
    41              x = j + 'a';
    42        }    
    43     }
    44     if(k >= 2){
    45         printf("Impossible
    ");
    46     }     
    47     else printf("%d
    ",change(str,x,len));
    48     return 0;
    49 } 
    View Code

               

  • 相关阅读:
    OPENGLES 绘制纹理带黑圈pre-multiplying
    listview相关代码整理
    时区列表
    (迪杰斯特拉)Dijkstra算法
    全源最短路径(Floyd算法)
    go配置私有仓库 (go mod配置私有仓库)
    mingw+gcc 10.1下载
    线段树应用及概况
    清理docker常用命令
    minio设置永久访问链接
  • 原文地址:https://www.cnblogs.com/chenzhiyuan/p/5191148.html
Copyright © 2011-2022 走看看