快轉到主要內容

CSES-1622 Creating Strings

標籤: 窮舉 STL
目錄

題目連結:https://cses.fi/problemset/task/1622


題意
#

題目會給我們一個字串(長度最多只有 8),我們要用字串裡現有的這些字母去重新排列,然後生出所有「不重複」而且「按照字典序排列」的字串組合。
輸出格式要求第一行必須先印出總共有幾種組合,接著再把這些組合一行一行照順序印出來。

思路
#

看到字串長度最多才 8,馬上就能察覺到:不管它裡面的字母怎麼大風吹,組合總數撐死最多也就是 \( 8! = 40320 \) 種。這個數字對現在高配版的電腦效能而言,用暴力窮舉連塞牙縫都不夠。

在強大的 C++ 裡面,只要遇到這種「列出所有排列組合」的窮舉題,二話不說直接把標準函式庫的 std::next_permutation 拿出來砸下去就對了。
這個函式超級好用,你只要把字串的起點跟終點丟給它,它就會自己幫把字串修改成下一個字典序剛好大一點的排列;如果已經大到不能再大了,它的回傳值就會直接變成 false,讓你順利跳出迴圈。

但要注意的是,它只能生出「比當前字典序還要大」的組合。這代表如果想要拿到「所有」的排列,剛讀進字串的時候,一定要先用 sort 把它排成最小的字典序。只要有先排好序,之後搭配一個 do-while 迴圈不斷呼叫它,就能把所有的排列都安全找出來了。

程式碼
#

#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define fi first
#define se second
#define INF LONG_LONG_MAX/1000
#define WA() cin.tie(0)->sync_with_stdio(0)
#define all(x) (x).begin(), (x).end()
#define int long long
#define PII pair<int, int>

signed main() { WA();
    string input;
    vector<string> v; // 儲存所有產生出來的排列組合
    int cnt = 0;      // 紀錄共有幾種組合
    cin >> input;      // 讀取初始字串

    // 必須先將字串以字典序由小到大排序,這是使用 next_permutation 的關鍵起手式
    sort(all(input));

    // 透過 do-while 迴圈,保證連第一個(最小的)排列也會被收錄進去
    do {
        v.pb(input);
        cnt++;
    } while (next_permutation(all(input))); // 自動產生下一個字典序更大的排列

    cout << cnt << '\n'; // 第一行輸出排列總數
    for (auto &s : v) cout << s << '\n'; // 接著把所有的排列結果一行一行印出來
}
Piau 的筆記本
作者
Piau 的筆記本
希望我寫下來的東西能夠長久的記在我的腦中