原題鏈接:http://poj.org/problem?id=3617
問題梗概:給定長度爲 的字符串 , 要構造一個長度爲 的字符串 。起初, 是一個空串,隨後反覆進行下列任意操作。
- 從 的頭部刪除一個字符,加到 的尾部。
- 從 的尾部刪除一個字符,加到 的尾部。
目的是要構造字典序儘可能小的字符串 。
限制條件:
- 字符串 只包含大寫英文字母
- 輸出的字符串每 80 個字符進行一次換行
字典序是指從前到後比較兩個字符串大小的方法。首先比較第 1 個字符,如果不同則第 1 個字符較小的字符串更小,如果相同則繼續比較第 2 個字符......如此繼續,來比較整個字符串的大小。
問題分析:從字典序的性質上看,無論 的末尾有多大,只要前面部分的較小就可以。所以我們可以試一下如下貪心算法:
- 不斷取 的開頭和末尾中較小的一個字符放到 的末尾。
這個算法已經接近正確了,只是針對 的開頭和末尾字符相同的情形還沒有定義。在這種情形下,因爲我們希望能夠儘早使用更小的字符,所以就要比較下一個字符的大小。下一個字符也有可能相同,因此就有如下算法:
- 按照字典序比較 和將 反轉後的字符串 ;
- 如果 較小,就從 的開頭取出一個文字,追加到 的末尾;
- 如果 較小,就從 的末尾取出一個文字,追加到 的末尾;
- 如果相同則取哪個都可以。
根據前面提到的性質,字典序比較類的問題經常能用得上貪心法。
代碼如下:
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX = 2000;
// 輸入
int N;
char S[MAX + 1];
void solve() {
// 剩餘的字符串爲 S[a], S[a+1],...., S[b]
int a = 0, b = N - 1, flag = 0;
while (a <= b) {
// 將從左起和從右起的字符串比較
bool left = false;
for (int i = 0; a + i <= b; i++) {
if (S[a + i] < S[b - i]) {
left = true;
break;
}
else if (S[a + i] > S[b - i]) {
left = false;
break;
}
}
if (left) cout << (S[a++]), flag++;
else cout << (S[b--]), flag++;
if (flag == 80) cout << endl, flag = 0;
}
cout << endl;
}
int main() {
while (cin >> N) {
for (int i = 0; i < N; i++)
cin >> S[i];
solve();
}
return 0;
}