基礎練習 完美的代價
時間限制:1.0s 內存限制:512.0MB
問題描述
回文串,是一種特殊的字符串,它從左往右讀和從右往左讀是一樣的。小龍龍認為回文串才是完美的。現在給你一個串,它不一定是回文的,請你計算最少的交換次數使得該串變成一個完美的回文串。
交換的定義是:交換兩個相鄰的字符
例如mamad
第一次交換 ad : mamda
第二次交換 md : madma
第三次交換 ma : madam (回文!完美!)
交換的定義是:交換兩個相鄰的字符
例如mamad
第一次交換 ad : mamda
第二次交換 md : madma
第三次交換 ma : madam (回文!完美!)
輸入格式
第一行是一個整數N,表示接下來的字符串的長度(N <= 8000)
第二行是一個字符串,長度為N.只包含小寫字母
第二行是一個字符串,長度為N.只包含小寫字母
輸出格式
如果可能,輸出最少的交換次數。
否則輸出Impossible
否則輸出Impossible
樣例輸入
5
mamad
mamad
樣例輸出
3
題目解析:
(1)Impossible 的兩種情況:
- n為奇數時,如果已經有一個字符出現的次數為奇數,還找到了一個字符出現的次數為奇數,那么就不能構成回文串;
- n為偶數時,只要找到有一個字符出現的次數為奇數,那么就不能構成回文串。
(2)題目中要求輸出最小的交換次數,那么怎樣才能保證交換次數最小呢?
如果 n 為偶數,那么從第一字符開始,從后往前找第一個和它相同的字符,如果找了,就將找到的字符交換到最后一個位置,在下一次遍歷時,就可以不用管剛才已經交換好的那來兩個字符;下一次從第二個字符開始,從倒數第二個字符開始遍歷,執行和上述相同的操作;
如果 n 為奇數,在字符串的某一個位置找到了那個出現次數為奇數的字符,我們不必將次字符現在就交換到中間位置,而是先計算它到中間位置需要交換的次數,然后累加到 cnt 中,將剩下的字符都交換到對稱后,再交換這個字符即可。
試着想一想,如果第一個字符就為出現次數為奇數的字符,那么將它交換到中間位置,接下來交換其他字符時,每次的交換次數都會多一次。這其實是一種普遍的規律。
示例代碼:
1 #include<iostream> 2 using namespace std; 3 4 int main() 5 { 6 int n; 7 cin >> n; 8 string s; 9 cin >> s; 10 11 int end = n - 1; //字符串最后一個字符 12 int cnt = 0; //交換次數 13 int oddNum = 0; //判斷是否已經有一個單獨的奇個數的字符了 14 15 for (int i = 0; i < end; i++)//從第一個字符到倒數第二個字符遍歷 16 { 17 for (int j = end; j >= i; j--)//從最后一個開始,到第i個字符,尋找與s[i]相同的字符 18 { 19 if (i == j) //如果沒找到 20 { 21 if (n % 2 == 0 || oddNum == 1) //不可能的兩種情況 22 { 23 cout << "Impossible"; 24 return 0; 25 } 26 oddNum = 1; //找到一個字符出現的次數為奇數 27 cnt += n / 2 - i; //將次字符交換到中間位置的次數 28 } 29 else if (s[i] == s[j]) //如果找到了,將s[j]交換到s[end]位置 30 { 31 for (int k = j; k < end; k++) //交換相鄰兩個位置的字符 32 { 33 swap(s[k], s[k+1]); 34 cnt++; 35 } 36 end--; //末尾遞減 37 break; //開始從i+1處重復操作 38 } 39 } 40 } 41 42 cout << cnt; 43 44 return 0; 45 }