トップページに戻る
次のC#のサンプルへ
前のC#のサンプルへ
8-17 辞書順式で次の順列を求める
問題
ある順列が与えられたとき、辞書式順で次の順列を求める方法があります。
●与えられた順列を右から左に見ていき、初めて小さくなった要素が変更すべき要素である。
例えば、与えられた順列が31420であれば、1が変更すべき要素である。
(その右の420は0と2と4でできる順列の辞書式順で最後のものであるため)
●変更すべき要素(1)の右の部分(420)にある要素で、
1の次に大きいもの(2)を探し、それを変更すべき要素と入れ替える。順列は32410となる。
ここで、410の部分を、0と1と4でできる順列の辞書式順で最初のものに置き換える。
(それには、単純に410を逆順に並べ替えればよい)
これが、最初に与えられた順列の、辞書式順の次の順列である。
これをプログラムにして下さい。
再帰の技法の31ページの練習問題を参考にさせていただきました。
ソース
using System;
class Program
{
static void Main()
{
//string CurrJyunretu = "31420";
string CurrJyunretu = "01234";
for (int I = 0; I <= 5 * 4 * 3 * 2; I++) {
string NextJyunretu = getNextJyunretu(CurrJyunretu);
Console.WriteLine("{0}の次の順列は、{1}", CurrJyunretu, NextJyunretu);
if (NextJyunretu.Contains("順列の最後")) return;
CurrJyunretu = NextJyunretu;
}
}
static string getNextJyunretu(string pTarget)
{
//右から左に見ていき、初めて小さくなった要素を求める
int WillChangeInd = -1;
char[] wkCharArr = pTarget.ToCharArray();
for (int I = wkCharArr.GetUpperBound(0) - 1; I >= 0; I--) {
if (wkCharArr[I] < wkCharArr[I + 1]) {
WillChangeInd = I;
break;
}
}
if (WillChangeInd == -1) return "なし(順列の最後に到達)";
//初めて小さくなった要素より右の変更すべき要素を求める
int MinCharInd = WillChangeInd + 1;
for (int I = WillChangeInd + 2; I <= wkCharArr.GetUpperBound(0); I++) {
if (wkCharArr[WillChangeInd] >= wkCharArr[I]) continue;
if (wkCharArr[MinCharInd] >= wkCharArr[I]) {
MinCharInd = I;
}
}
//変更すべき要素と3角交換
char WKChar = wkCharArr[WillChangeInd];
wkCharArr[WillChangeInd] = wkCharArr[MinCharInd];
wkCharArr[MinCharInd] = WKChar;
//正順で連結
var sb = new System.Text.StringBuilder();
for (int I = 0; I <= WillChangeInd; I++) {
sb.Append(wkCharArr[I]);
}
//逆順で連結
for (int I = wkCharArr.GetUpperBound(0); I >= WillChangeInd + 1; I--) {
sb.Append(wkCharArr[I]);
}
return sb.ToString();
}
}
ソース (深さ優先探索で順列を列挙してから、次の順列を求める)
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main()
{
string wk = "01234";
Console.WriteLine("{0}の次の順列は{1}", wk, getNextJyunretu(wk));
}
static string getNextJyunretu(string pTarget)
{
var query = pTarget.ToCharArray().OrderBy(X => X);
string BaseStr = "";
foreach (char each in query) {
BaseStr += each;
}
var stk = new Stack<string>();
for (int I = 0; I <= BaseStr.Length - 1; I++) {
stk.Push(BaseStr[I].ToString());
}
var answerList = new List<string>();
while (stk.Count > 0) {
string wkStr = stk.Pop();
if (wkStr.Length == BaseStr.Length) {
answerList.Add(wkStr);
continue;
}
for (int I = 0; I <= BaseStr.Length - 1; I++) {
if (wkStr.Contains(BaseStr[I].ToString()) == false) {
stk.Push(wkStr + BaseStr[I].ToString());
}
}
}
foreach (string each in answerList.OrderBy(X => X)) {
if (pTarget.CompareTo(each) >= 0) continue;
return each;
}
return pTarget;
}
}
実行結果
01234の次の順列は01243
解説
深さ優先探索で順列を列挙してから、次の順列を求める方法も作ってみました。