{ }codelper
递归暴力枚举P0100

字母排列

一行给出一个由英文小写字母组成的单词 s。
把 s 的每个字母恰好用一次能组成的所有不同单词,按字典序(从第一个字母开始比较,字母靠前的在前)每行一个全部输出。
如果 s 中有重复字母,同一个单词可能出现多次,只输出一次(aab 时为 aab、aba、baa)。

输入
1 ≤ s 的长度 ≤ 7
输出
所有不同的单词,按字典序,每行一个
样例 1 · stdin
aab
stdout
aab
aba
baa
样例 2 · stdin
cab
stdout
abc
acb
bac
bca
cab
cba
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

一个递归同时解决重复和顺序。
先数好每个字母剩几个,在每个位置从 a 开始依次选一个“剩下的字母”,再进入下一个位置(选完后把个数恢复)。
同一个字母在一个位置只选一次,所以不会再生成相同的单词;从小字母开始选,自然就是字典序。
常见陷阱有两个。
Python itertools.permutations 区分的是位置而不是字母,所以 aab 会输出两次 aab。
用交换位置(swap)的方法,即使去掉重复也不是字典序。
工具也因语言而异。
C++ std::next_permutation 从排好序的字母开始,会按字典序无重复地给出下一个排列(不用递归);其他语言没有这样的标准函数。
7 个字母最多有 5,040 行,所以 Java、C# 先收集到 StringBuilder 再一次写出。
Craykon 中函数 늘어놓기(prefix) 调用自己,到底时用 返回; 返回。
各行收集到字符串 out 中,用 控制台.接着输出(out) 一次写出。

Pythonmain.py · 22 行
import sys s = input().strip()letters = sorted(set(s))  # 쓸 수 있는 글자(작은 것부터)left = {c: s.count(c) for c in letters}  # 글자마다 남은 개수out = []  def arrange(prefix):    if len(prefix) == len(s):        out.append(prefix)        return    # 같은 글자는 이 자리에서 한 번만 고르니 겹친 줄이 생기지 않고, 작은 글자부터 고르니 사전 순이 된다    for c in letters:        if left[c] > 0:            left[c] -= 1            arrange(prefix + c)            left[c] += 1  arrange("")sys.stdout.write("\n".join(out) + "\n")
JavaScriptmain.js · 22 行
const s = require("fs").readFileSync(0, "utf8").trim();const letters = [...new Set(s)].sort(); // 쓸 수 있는 글자(작은 것부터)const left = new Map(letters.map((c) => [c, [...s].filter((x) => x === c).length]));const out = []; function arrange(prefix) {  if (prefix.length === s.length) {    out.push(prefix);    return;  }  // 같은 글자는 이 자리에서 한 번만 고르니 겹친 줄이 생기지 않고, 작은 글자부터 고르니 사전 순이 된다  for (const c of letters) {    if (left.get(c) > 0) {      left.set(c, left.get(c) - 1);      arrange(prefix + c);      left.set(c, left.get(c) + 1);    }  }} arrange("");console.log(out.join("\n"));
TypeScriptmain.ts · 24 行
import { readFileSync } from "node:fs";const s: string = readFileSync(0, "utf8").trim();const letters: string[] = [...new Set(s)].sort(); // 쓸 수 있는 글자(작은 것부터)const left = new Map<string, number>(letters.map((c) => [c, [...s].filter((x) => x === c).length]));const out: string[] = []; function arrange(prefix: string): void {  if (prefix.length === s.length) {    out.push(prefix);    return;  }  // 같은 글자는 이 자리에서 한 번만 고르니 겹친 줄이 생기지 않고, 작은 글자부터 고르니 사전 순이 된다  for (const c of letters) {    const k = left.get(c)!;    if (k > 0) {      left.set(c, k - 1);      arrange(prefix + c);      left.set(c, k);    }  }} arrange("");console.log(out.join("\n"));
JavaMain.java · 31 行
import java.util.Scanner; public class Main {    static int[] left = new int[26]; // 글자마다 남은 개수    static char[] path;    static StringBuilder out = new StringBuilder();     static void arrange(int depth) {        if (depth == path.length) {            out.append(path).append('\n');            return;        }        // 같은 글자는 이 자리에서 한 번만 고르니 겹친 줄이 생기지 않고, a부터 고르니 사전 순이 된다        for (int c = 0; c < 26; c++) {            if (left[c] > 0) {                left[c]--;                path[depth] = (char) ('a' + c);                arrange(depth + 1);                left[c]++;            }        }    }     public static void main(String[] args) {        String s = new Scanner(System.in).next();        for (char ch : s.toCharArray()) left[ch - 'a']++;        path = new char[s.length()];        arrange(0);        System.out.print(out);    }}
C#Main.cs · 38 行
using System;using System.Text; class Program{    static int[] left = new int[26]; // 글자마다 남은 개수    static char[] path;    static StringBuilder output = new StringBuilder();     static void Arrange(int depth)    {        if (depth == path.Length)        {            output.Append(path).Append('\n');            return;        }        // 같은 글자는 이 자리에서 한 번만 고르니 겹친 줄이 생기지 않고, a부터 고르니 사전 순이 된다        for (int c = 0; c < 26; c++)        {            if (left[c] > 0)            {                left[c]--;                path[depth] = (char)('a' + c);                Arrange(depth + 1);                left[c]++;            }        }    }     static void Main()    {        string s = Console.ReadLine().Trim();        foreach (char ch in s) left[ch - 'a']++;        path = new char[s.Length];        Arrange(0);        Console.Write(output);    }}
C++main.cpp · 32 行
#include <array>#include <iostream>#include <string> std::array<int, 26> left{}; // 글자마다 남은 개수std::string path, out; void arrange(std::size_t depth) {    if (depth == path.size()) {        out += path;        out += '\n';        return;    }    // 같은 글자는 이 자리에서 한 번만 고르니 겹친 줄이 생기지 않고, a부터 고르니 사전 순이 된다    for (int c = 0; c < 26; c++) {        if (left[c] > 0) {            left[c]--;            path[depth] = static_cast<char>('a' + c);            arrange(depth + 1);            left[c]++;        }    }} int main() {    std::string s;    std::cin >> s;    for (char ch : s) left[ch - 'a']++;    path.assign(s.size(), ' ');    arrange(0);    std::cout << out;}
Cmain.c · 32 行
#include <stdio.h>#include <string.h> static int left[26]; // 글자마다 남은 개수static char path[8];static int n; static void arrange(int depth) {    if (depth == n) {        puts(path);        return;    }    // 같은 글자는 이 자리에서 한 번만 고르니 겹친 줄이 생기지 않고, a부터 고르니 사전 순이 된다    for (int c = 0; c < 26; c++) {        if (left[c] > 0) {            left[c]--;            path[depth] = 'a' + c;            arrange(depth + 1);            left[c]++;        }    }} int main(void) {    char s[8];    scanf("%7s", s);    n = strlen(s);    for (int i = 0; i < n; i++) left[s[i] - 'a']++;    path[n] = '\0';    arrange(0);    return 0;}
Craykonmain.cray · 38 行
// 같은 글자는 이 자리에서 한 번만 고르니 겹친 줄이 생기지 않고, 작은 글자부터 고르니 사전 순이 된다늘어놓기(prefix){    如果 (prefix.长度 == s.长度)    {        out += prefix + "\n";        返回;    }    循环 (i = 0; i < letters.数量; i++)    {        如果 (left[i] > 0)        {            left[i] -= 1;            늘어놓기(prefix + letters[i]);            left[i] += 1;        }    }} s = 控制台.读行().去空白();// 쓸 수 있는 글자(작은 것부터)와 글자마다 남은 개수letters = [];遍历 (c 在 s){    如果 (!letters.包含(c))    {        letters.添加(c);    }}letters.排序();left = 新列表(letters.数量, 0);遍历 (c 在 s){    left[letters.查找位置(c)] += 1;}out = "";늘어놓기("");控制台.接着输出(out);