15

次の再考の要件を満たす最適な文字列ジェネレータ クラスを実装することが実現可能かどうか疑問に思います。


  • を使用した生成基準
  • 辞書順列挙。
  • プロパティを数える
  • 索引付けされたアクセス

正規表現に慣れていません。最初のコードを思いつくことはできませんが、TList を基本クラスとして使用する単純な実装を考え、「ブルート フォース」で生成された文字列に対してフィルター (正規表現) を使用します。

他の最適な選択肢は何ですか?


  • 順序: 最初に長さ (短い順)、次に辞書順。
  • 生成に使用される文字の範囲の指定: [AZ]、[az]、数字、特殊記号、および最終的にはスペース (regex ?) のすべての印刷可能または可能な組み合わせ。
  • 指定された最小/最大で制限された文字列の長さ。
  • 境界で制限された検索スペース: フィルタリングの可能性がある開始文字列と終了文字列 (正規表現 ?)

最終編集

まず、 regex の代わりにregex likeを使用してヘッダーを言い換えました。

最初の要件はオープン ドアであり、扱いにくい問題につながる可能性があるため、修正を検討しています。

正しい言葉遣いについての提案と助けが必要です。

考え直した要件の編集が完了しました。改良のための提案はまだ受け付けています。

4

2 に答える 2

4

古い質問ですが、誰も答えていません。賞金はまだ有効で、解決策はすでに用意されているので、次のような回答が考えられます。

私はかつてこれを行う小さなプログラムを書きました。ただし、C++/Qt であり (ほとんどすべてのプログラムを Delphi で記述していますが、それは C++ で記述しています)、Unicode をサポートしておらず、順序の保証もありません。

次のように機能します。

  1. 全て ?{} + * | () 演算子は (最大限に) 展開されるため、文字クラスと後方参照のみが残ります。

    例えば[a-c]+|t*|([x-z]){2}foo\1|(a|b)(t|u) _[a-c]|[a-c][a-c]|[a-c][a-c][a-c]|[a-c][a-c][a-c][a-c]||t|tt|tt|ttt|ttt|([x-z][x-z])foo\1|at|au|bt|bu

    (後者の式の | は単なる表記であり、プログラムは各代替部分正規表現をリストに保持します)

  2. 複数の文字への後方参照は、単一の文字への後方参照に置き換えられます。

    たとえば、上記の式は次のようになります[a-c]|[a-c][a-c]|[a-c][a-c][a-c]|[a-c][a-c][a-c][a-c]||t|tt|tt|ttt|ttt|([x-z])([x-z])foo\1\2|at|au|bt|bu

    これで、各代替部分正規表現は固定長の文字列に一致します。

  3. 選択肢ごとに、クラスから文字を選択するすべての組み合わせが出力されます。

    たとえば、上記の式は次のようになりますa|b|c|aa|ba|..|cc|aaa|baa|...|ccc|aaaa|...|cccc||t|tt|tt|ttt|ttt|xxfooxx|yxfooyx|...|zzfoozz|at|au|bt|bu

おそらく、次のように shortlex 順序保証を追加できます。

  1. クラス内の文字をアルファベット順に並べ替えます

  2. 上記のステップ 2. で取得した選択肢を長さで並べ替えます。

    (指数関数的に多くの選択肢がありますが、通常、それらの数は結果の文字列の数と比較して無視できます)

  3. 文字クラスと後方参照をソート/交換して、すべての参照が後方を指すようにする

  4. 以前のように、単一の固定長の代替文字列の可能な文字列を列挙しますが、最初の文字クラスではなく、最後の文字クラスから開始して、アルファベット順に並べ替えます。

    (前方を指す後方参照がある場合、これは機能しません)

  5. 同じ長さの選択肢が複数ある場合は、それらを「並列」に列挙し、現在の文字列を比較して、アルファベット順に最も小さいものを出力します。(つまり、各選択肢のソート済みリストをマージします。)

    これは、順序に影響を与えずに列挙できる接尾辞の個別の接頭辞と安全な文字クラスを検出するなどして、最適化できます。(たとえば、a[az]|b[az] には異なる接頭辞があり、[az] は比較なしで列挙できます)

于 2012-07-25T17:35:32.597 に答える
3

これは、言語の最小の決定論的有限オートマトンを構築することで行います。正規表現から始めている場合、これはトンプソンの構築とそれに続くサブセットの構築と最小化によって自動的に行うことができます。たとえば、この説明を参照してください。

DFA があれば、次のようなアルゴリズムを使用できます。

Let P = { < START, [""] > } be a set of pairs <State, list of strings>
for n = 0, 1, ... Max
  Let P' = {} be a new set 
  while P is not empty 
    Remove the pair <s, L> from P 
    For each transition s -- c --> t in alpha order of c
      if t is an accepting state,
          output l + c for each string l in L
      put <t, L + c> in P' (** i.e. append c to each string in L)
  end
  Set P = P'
end

**重複が簡単に発生する可能性があるため、マークされたステップは真のセット挿入である必要があることに注意してください。

これはコアアルゴリズムです。P出力の長さに応じて指数関数的に大きくなる可能性がありますが、これは将来の出力文字列のすべての可能性を追跡するための代償にすぎません。あなたが言及した順序/サイズ/スペースの制約は、リストLでソートされた順序を維持し、リソースの制限に達したときに検索を切断することで確保できます。

編集

これは、オプションのマイナス記号を使用して、単純なバイナリ浮動小数点リテラルの DFA をハードコーディングしたおもちゃの Java の例です。これは、上記の疑似コードとは少し異なるスキームを使用して、厳密にソートされた出力順序を取得し、文字範囲に対応します。

import java.util.Comparator;
import java.util.TreeSet;

public class Test{

    public static class DFA {

        public static class Transition  {

            final int to;
            final char lo, hi; // Character range.

            public Transition(int to, char lo, char hi) {
                this.to = to;
                this.lo = lo;
                this.hi = hi;
            }

            public Transition(int to, char ch) {
                this(to, ch, ch);
            }
        }

        // transitions[i] is a vector of transitions from state i.
        final Transition [] [] transitions;

        // accepting[i] is true iff state i is accepting
        final boolean [] accepting;

        // Make a fresh immutable DFA.
        public DFA(Transition [] [] transitions, boolean [] accepting) {
            this.transitions = transitions;
            this.accepting = accepting;
        }

        // A pair is a DFA state number and the input string read to get there.
        private static class Pair {
            final int at;
            final String s;

            Pair(int at, String s) {
                this.at = at;
                this.s = s;
            }
        }

        // Compare pairs ignoring `at` states, since 
        // they are equal iff the strings are equal.
        private Comparator<Pair> emitOrder = new Comparator<Pair>() {
            @Override
            public int compare(Pair a, Pair b) {
                return a.s.compareTo(b.s);
            }
        };

        // Emit all strings accepted by the DFA of given max length.
        // Output is in sorted order.
        void emit(int maxLength) {
            TreeSet<Pair> pairs = new TreeSet<Pair>(emitOrder);
            pairs.add(new Pair(0, ""));
            for (int len = 0; len <= maxLength; ++len) {
                TreeSet<Pair> newPairs = new TreeSet<Pair>(emitOrder);
                while (!pairs.isEmpty()) {
                    Pair pair = pairs.pollFirst();
                    for (Transition x : transitions[pair.at]) {
                        for (char ch = x.lo; ch <= x.hi; ch++) {
                            String s = pair.s + ch;
                            if (newPairs.add(new Pair(x.to, s)) && accepting[x.to]) {
                                System.out.println(s);
                            }
                        }
                    }
                }
                pairs = newPairs;
            }
        }
    }

    // Emit with a little DFA for floating point numbers.
    public void run() {
        DFA.Transition [] [] transitions = {
            {   // From 0
                new DFA.Transition(1, '-'),
                new DFA.Transition(2, '.'),
                new DFA.Transition(3, '0', '1'),
            },
            {   // From 1
                new DFA.Transition(2, '.'),
                new DFA.Transition(3, '0', '1'),
            },
            {   // From 2
                new DFA.Transition(4, '0', '1'),
            },
            {   // From 3
                new DFA.Transition(3, '0', '1'),
                new DFA.Transition(4, '.'),
            },
            {   // From 4
                new DFA.Transition(4, '0', '1'),
            }  
        };
        boolean [] accepting = { false, false, false, true, true };
        new DFA(transitions, accepting).emit(4);
    }

    public static void main (String [] args) {
        new Test().run();
    }
}
于 2012-07-30T19:21:08.453 に答える