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
2
这时继续固定字符,固定下标为 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
3
这时,继续执行第一层循环,发现 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,代表此时已经发现了两个不可匹配的字符,那么该字符串不可能为回文串。