複数の単語で異なる同じ文字をフィルタリングします。

複数の単語で異なる同じ文字をフィルタリングします。

私は非常に大きな単語のリストを持っています。 Unixを使用して特定の文字共有基準に一致する複数の単語のインスタンスを見つける方法は?たとえば、単語1と2に同じ4番目と7番目の文字があり、単語2と3に同じ4番目と9番目の文字があり、単語3と4に同じ2番目、4番目、9番目の文字があります。したいです。

例:

aaadiigjlf
abcdefghij
aswdofflle
bbbbbbbbbb
bisofmlwpa
fsbdfopkld
gikfkwpspa
hogkellgis

戻ることができる

abcdefghij
aaadiigjlf
fsbdfopkld
aswdofflle

明確にするために、特定の場所で同じ文字を共有する単語を返すコードが必要です(例に示されている「d」と「g」)。また、すべての基準に一致しない単語を返すことを願っています。たとえば、与えられた例では、単語1と4は4番目の文字を共有しますが、必ずしも2番目、7番目、9番目の文字を共有するわけではありません。完成した形式で実行されているプログラムの場合、厳密な文字共有基準である9つに基づいて、非常に小さい単語のリスト(おそらく10のみ)が返されると予想されます。

編集者:いいですね。カードはテーブルの上にあります。それが私がどのようにして得るのかという問題です。

私は単語リストを受け取り、そのリストには次のようにグリッドに入れることができる10個の文字単語が10個あると言われました。

-112--3---
---2--3-4-
-5-2----4-
-5-2--6-4-
75-2--6---
75---8----
7----8----
79---8----
-9--0-----
-9--0---xx

すべての単語を読んでください。同じ数字(およびx)(すべて1、両方2など)を持つすべてのスペースは同じ文字です(他の数字は同じ文字にすることができますが、必ずしもそうではありません)。

更新:私はまだRalphのコードを実行しています。今は完了している可能性がありますが、外付けハードドライブに障害が発生した後にプロセスを再起動する必要がありました。ほぼ48時間が経過しましたが、まだ進行速度が遅いです。

ベストアンサー1

ファイルのリストを複数回処理することは避けられませんが、各ルールを一度だけ処理すれば十分です。主なプロセスは、可能な「単語リスト」を拡張しながら、単語を10回繰り返すことです。ここで、各リストについて、i:番目の単語はそのリストのi:番目のルールと一致します。各単語がリストと一致すると、その単語が追加され、リストが展開されます。

bash:Rこのデータ構造を維持するには少し弱いですが、オプションで、「単語リスト」を拡張リストに適用する次の規則を表すカンマ区切りの単語シーケンスとして表すことができます。RこれはRもちろん、リストの単語数に1を加えたものと同じです。これを基本データ構造として使用すると、次の基本プロセスが表示されることがあります。

N=0
M=0
cat $1 $1 $1 $1 $1 $1 $1 $1 $1 $1 | while read w || ending ; do
    [ -z "$F" ] && F=$w # capture the first word                                
    [ "$F" = "$w" ] && N=$((N+1)) # count first word appearances                
    Q=( )
    matches $w 1 "" && Q=( ${w}:2 )
    for p in ${P[@]} ; do
        A="${Q[@]}" && [ "${A/$p/}" = "${A}" ] || continue # if duplicate       
        R=${p#*:} && [ $R -lt $M ] && continue # if path too short              
        Q=( ${Q[@]} $p ) # preserve this path for next word                     
        [ "${p/$w/}" = "$p" ] || continue # if word already in path             
        p=${p%:*} # p is now the word list only                                 
        if matches $w $R $p ; then
            Q=( ${Q[@]} $p,${w}:$((R+1)) )
            M=$N
        fi
    done
    P=( ${Q[@]} )
done

これは、単語がルールリストの適切な拡張であるかどうかmatchesを判断するためのルールの操作表現です。w次のようなもの(メインプログラムの前にあります):pR

matches() {
    local w=$1
    local p=$3
    case $2 in
        1) # -112--3---
            eqchar $w 2 $w 3
            ;;
        2) # ---2--3-4-
            eqchar $w 4 $p 4 && eqchar $w 7 $p 7
            ;;
        3) # -5-2----4-
            eqchar $w 4 $p 4 && eqchar $w 9 $p $((11+9))
            ;;
        4) # -5-2--6-4-
            eqchar $w 2 $p $((22+2)) && eqchar $w 4 $p 4 &&
              eqchar $w 9 $p $((11+9))
            ;;
        5) # 75-2--6---
            eqchar $w 2 $p $((22+2)) && eqchar $w 4 $p 4 &&
              eqchar $w 7 $p $((11+7))
        ;;
        6) # 6: 75---8----
            eqchar $w 1 $p $((44+1)) && eqchar $w 2 $p $((22+2)) &&
              eqchar $w 7 $p $((33+7))
            ;;
        7) # 7: 7----8----
            eqchar $w 1 $p $((44+1)) && eqchar $w 6 $p $((55+6))
            ;;
        8) # 8: 79---8----
            eqchar $w 1 $p $((44+1)) && eqchar $w 6 $p $((55+6))
            ;;
        9) # 9: -9--0-----
            eqchar $w 2 $p $((77+2))
            ;;
        10) # 10: -9--0---xx
            eqchar $w 2 $p $((77+2)) && eqchar $w 5 $p $((88+5)) &&
              [ -z "${1#*xx}" ]
            ;;
        *)
            return 1
            ;;
    esac
}

このeqchar関数は、特定の位置にある最初の文字列の文字が、特定の位置にある2番目の文字列の文字と一致するかどうかをテストします。後者の文字列はコンマで区切られた順序の先頭の単語であるため、i*11+jj:番目の文字(1ベース)からi:番目の単語(0ベース)へのインデックス付け方法を許可します。たとえば、インデックスは$((77+2))8番目の単語の2番目の文字です。

eqchar() {
    local w=$1
    local p=$3
    [ "${w:$(($2-1)):1}" = "${p:$(($4-1)):1}" ]
}

関数は関数の前に宣言する必要がeqcharあり、基本プロシージャの前に宣言する必要があります。matches

最後に、メインプログラムにはending最後に結果を印刷する機能が含まれています。予想される結果は、P長さ10の「単語リスト」を保存することですが、通常、ルールに適合するP可能matchesな限り長い単語のリストは実際にはすべて保存されます。関数endingは必要な印刷物を生成して返し、句を終了する必要が1ありますwhile

これは、O(N)(またはO(N * T)、Tは非常に高い場合は最初の規則と一致する数)を使用する「純粋な」bashソリューションです。

おすすめ記事