バックトラックで 文字列を 回文だけに 区切る 方法を 全部集める
文字列を、どの
海外で定番の問題リスト 4 本には入っていません
問題
英小文字のs をs を
- 1 字だけの
部分は、 いつも 回文です。 その ため、 少なくとも 1 字ずつに 切る 方法は 必ずあります - 切り分け方
どうしの 順は、 1 つ目の 部分が 短い方を 先にします。 1 つ目が 同じなら 2 つ目が 短い方を 先に、と後ろへ 続けて比べます sの長さは 1 以上 12 以下です
入力と出力の例
例 1
1 oo をnoon を
例 2
1 字なので、
例 3
aya と kayak もk のkayak が来ます。
面接での聞かれ方
面接では、
- 「文字列を、
全部の 部分が 回文に なるように 分割する 方法を 全部返してください」 - 書き終えると
「回文か どうかを 毎回確かめていますが、 前もって 表に できますか?」と、 同じ 判定の 繰り返しを 減らす工夫を 聞かれます - 「分け方が
何通り ありうるか」から、 最悪の 計算量を 言えるかも 見られます
返す順と、
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「再帰と木」に、
関わる学び
再帰は、
まず 考える手順
段 1
長さ n の
段 2
頭から
段 3
1 つ目の
段 4
回文か
解答
Python
Python
def palindrome_parts(s):
n = len(s)
out = []
path = []
def walk(i):
if i == n:
out.append(path[:])
return
for j in range(i, n):
w = s[i:j + 1]
if w != w[::-1]:
continue
path.append(w)
walk(j + 1)
path.pop()
walk(0)
return outJavaScript
JavaScript
function palindromeParts(s) {
const out = [];
walk(s, 0, [], out);
return out;
}
function walk(s, i, path, out) {
if (i === s.length) {
out.push([...path]);
return;
}
const n = s.length;
for (let j = i; j < n; j++) {
if (!isPal(s, i, j)) {
continue;
}
const w = s.slice(i, j + 1);
path.push(w);
walk(s, j + 1, path, out);
path.pop();
}
}
function isPal(s, i, j) {
while (i < j) {
if (s[i] !== s[j]) {
return false;
}
i++;
j--;
}
return true;
}Go
Go
func palindromeParts(
s string,
) [][]string {
n := len(s)
out := [][]string{}
path := []string{}
var walk func(i int)
walk = func(i int) {
if i == n {
p := []string{}
p = append(p, path...)
out = append(out, p)
return
}
for j := i; j < n; j++ {
if !isPal(s, i, j) {
continue
}
w := s[i : j+1]
path = append(path, w)
walk(j + 1)
path = path[:len(path)-1]
}
}
walk(0)
return out
}
func isPal(
s string, i, j int,
) bool {
for i < j {
if s[i] != s[j] {
return false
}
i++
j--
}
return true
}JavaScript と Go は、
計算量
- 時間は O(2^n × n) です。
切り分け方は 多くて 2^(n-1) 通り (全部同じ字の とき) で、 1 つごとに 回文の 確かめと 写しに n まで かかります - 使う
記憶は、 答えを 除くと O(n) です。 再帰の 深さと、 ここまでの 部分の 並びが n までです
よく ある間違い
- 回文でない
部分の あとも続ける。 全部の 切り方を 作ってから 確かめると、 作らなくて よい 枝まで 作ります。 1 つ目の 部分が 回文でなければ、 その 時点で 次の 長さへ移ります - ここまでの
部分を 写さずに 答えに入れる。 答えの 中の 並びが 全部同じ物を 指し、 最後には 空の 並びばかりになります - 切り出しの
範囲が 1 字ずれる。 Python の s[i:j + 1]と JavaScript のslice(i, j + 1)は、j 字目を 含みます。 jで止めると、 1 字の 部分が 作れません - 長い
部分から試す。 答えは 全部 そろいますが、 この 問題で 決めた 順 (1 つ目が 短い方が 先) と 逆に並びます
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「頭から、
1 つ目の 部分の 長さを 短い方から 順に 試します。 その 部分が 回文なら、 残りを 同じように 切る 再帰に 進み、 残りが 空に なったら 答えに 足します。 回文でなければ、 その 長さは 飛ばします。 切り分け方は 多くて 2 の n - 1 乗通りなので、 時間は O(2^n × n) です。」
書き終えたら、
「例 1 の noon では、
1 つ目を n に して 残りの oon を 切ります。 o、 o、 n と 切って 1 つ目の 答え、 oo、 n と 切って 2 つ目です。 oon は 回文ではないので 飛ばします。 頭に 戻って、 no と noo は 回文でないので 飛ばし、 noon は 回文なので 3 つ目の 答えです。」