完美的代价

Description

回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。

小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。

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

例如: mamad

第一次交换 ad : mamda;

第二次交换 md : madma;

第三次交换 ma : madam (回文!完美!)

Input

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

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

Output

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

否则输出Impossible。

Sample Input

5
mamad

Sample Output

3

 

#include<stdio.h>
int main(){
    int n;//代表要输入的字符长度
    int flag=0;//用来标记是否出现了不能匹配的单个字符
    int count=0;// 用来表示最少的交换次数
    int j;//用来盛放n-1的值,仅是为了方便
    char a[8005];//用来盛放字符串
    char t;//用于字符的交换
    scanf("%d",&n);//输入n
    scanf("%s",a);//输入字符串
    j=n-1;
    for(int i=0;i<j;i++){//从下标为i的字符开始,到下标为【j-1】的字符结束,依次固定字符
        for(int k=j;k>=i;k--){//然后从下标为j的字符开始,到下标为i的结束(也就是从后往前)依次与固定的字符作比较
             if(i==k){//当固定的字符在字符串中没有找到能与它匹配的字符时
                if(n%2==0||flag){//判断字符串的长度是不是偶数,或者flag的值是否为1
                    printf("Impossible");//如果满足这两个条件中的任意一个,那么这个字符不可能是回文串,输出Impossible结束即可
                    return 0;
                }
                flag=1;//如果字符串长度为奇数并且flag的值为0,就将flag赋值为1 ,代表出现了不能匹配的单个字符
                count+=n/2-i;//count的值加上n/2-i ,n/2-i代表这个单个字符移到字符串中间位置的步数
            }
            else if(a[i]==a[k]){// 如果两个字符相同,即固定的字符在后面找到了与它能匹配的字符
                for(int b=k;b<j;b++){//将a[k]这个字符移到a[j]的位置
                    t=a[b];//交换过程
                    a[b]=a[b+1];
                    a[b+1]=t;
                    count++;//每交换一次,count自增1
                }
                j--;//当固定的字符找到能与它匹配的字符时,j自减1 ,这一步很关键
                break;// 当固定的字符找到能与它匹配的字符时,跳出第二层循环,第一层循环继续固定下一个字符
            }
        }
    }
    printf("%d",count);//输出count的值
    return 0;
}

 

 

 


有几个问题详细解释一下:

 

一:  如果在回文串里有两个字符相同,我们就暂且称呼这两个字符相互匹配

 

二:  回文串的特点:如果回文串的长度为偶数,那么回文串里任何一个字符都能找到与之匹配的字符,如果回文串的长度为奇数,那么回文串里只能有一个字符是不能匹配的,大家仔细想一想这个特点。

 

三:关于代码中第 25 行为何要判断长度是否为偶数:当出现不能匹配的字符时,才会判断这个条件。因为回文串长度为偶数时,任何一个字符都是可匹配的,所以,当字符串中出现不可匹配的字符并且字符长度为偶数时,该字符串一定不是回文串。只有奇数长度的回文串才会出现不可匹配的字符,并且只能有一个不能匹配的字符。

 

四:关于代码中第 25 行为何要判断 flag 是否为 1:当出现不能匹配的字符时,才会判断这个条件。flag  为  1  代表字符串中已经出现了一个不可匹配的字符,当出现不能匹配的字符并且 flag 为 1 时,代表这个字符串里有两个不可匹配的字符,这与回文串的特点不符,所以该字符串一定不是回文串。

 五:j--与  i++几乎是同时发生的,我们可以这样理解,当字符串中的第一个字符与最后一个匹配好了的话,我们就暂时将他们舍弃,先不管他们,只看他们中间的字符就行,

 

 

 

 

用几个例子来理解一下过程:

 

样例一:mamad

 

1

 首先在这里,代码中的 n 为 5,j 为 4,i 为 0,然后固定下标为 i 的字符,此时 i 为 0,即字符 m,然后从下标为 j 的字符开始,也就是从字符 d 开始,依次向前与 m 作比较,

字符 d 与字符 m 不相同,继续向前走,

走到字符 a字符 a 与字符 m 仍然不相同,继续向前走,

走到字符 m字符 m 与字符 m 相同,

这时,将第二个 m 移到下标为 j 的字符的地方,也就是字符 d 那里,首先 m 与 a 交换位置,count 自增 1,字符串变为 maamd,然后 m 与 d 交换位置,count 自增1,字符串变为 maadm,交

换步骤完成这时,j 会自减 1,j 变为 3,同时 break 跳出第二层循环,第一层循环的 i 自增 1,i 变为1

 

这时继续固定字符,固定下标为 i 的字符,此时 i 为 1,也就是字符 a,然后从下标为 j的字符开始,此时 j 为 3,也就是从字符 d 开始,依次向前与 a 作比较

 

字符 d 与字符 a 不相同,继续向前走,走到字符 a

 

字符 a 与字符 a 相同,这时,将第二个 a 移到下标为 j 的地方,也就是字符 d 那里,字符 a与字符 d 交换位置,count 自增 1,字符串变为 madam,交换步骤完成

 

这时,j 会自减 1,j 变成 2,同时 break 跳出第二层循环,第一层循环的 i 自增 1,i 变为2

 

这时,继续执行第一层循环,发现 i=j=2,不符合循环条件,跳出循环,输出 count 的值,

 

count 共自增三次,所以输出 3.

 

样例二:accc

 

首先在这里,代码中的 n 为 4,j 为 3,i 为 0,然后固定下标为 i 的字符,此时 i 为 0,即字符 a,然后从下标为 j 的字符开始,也就是从字符 c 开始,依次向前与 a 作比较,

 

字符 c 与字符 a 不相同,继续向前走,走到字符 c

 

字符 c 与字符 a 不相同,继续向前走,走到字符 c

 

字符 c 与字符 a 不相同,继续向前走,走到字符 a

 

这时,代码中 i 的值与 k 的值相同,字符 a 在字符串中找不到能与它匹配的字符,这时,判断字符长度是否为偶数或者  flag  是否为  1,这时发现字符长度为偶数,那么该字符串一定不是回文串。

 

样例三:abc

 

1 

首先在这里,代码中的 n 为 3,j 为 2,i 为 0,然后固定下标为 i 的字符,此时 i 为 0,即字符 a,然后从下标为 j 的字符开始,也就是从字符 c 开始,依次向前与 a 作比较

字符 c 与字符 a 不相同,继续向前走,走到字符 b

 

字符 b 与字符 a 不相同,继续向前走,走到字符 a

 

这时,发现字符 a 在字符串中找不到能与它匹配的字符,这时,判断字符长度是否为偶数或者 flag 是否为 1,这时发现字符长度为奇数,并且 flag 为初始值 0,这时将 flag 赋值为1,count+=   n/2-i,n/2-i 这里的值为 1,代表将字符 a 移到字符串中间所需要的步骤,注意一下,这时并不用真的将字符 a 移到字符串中间,只需加上所需步骤即可

 

2

 这时,i 自增 1,变为 1,j 仍为 2,然后继续固定下标为 i 的字符,即固定字符 b,从下标为 j 的字符开始,也就是从字符 c 开始,依次向前与 b 作比较

字符 c 与字符 b 不相同,继续向前走,走到字符 b

 

这时,i 的值与 k 的值相同,判断字符长度是否为偶数或者 flag 是否为 1,发现字符长度为奇数,flag 为 1,代表此时已经发现了两个不可匹配的字符,那么该字符串不可能为回文串。


posted @ 2020-01-22 22:09  ice--cream  阅读(774)  评论(0编辑  收藏  举报