过滤多个单词中不同的相同字符

过滤多个单词中不同的相同字符

我有一个非常大的单词表。如何使用 Unix 查找符合特定字符共享标准的多个单词的实例?例如,我希望单词 1 和 2 具有相同的第四个和第七个字符,单词 2 和 3 具有相同的第四个和第九个字符,单词 3 和 4 具有相同的第二个、第四个和第九个字符。

例子:

aaadiigjlf
abcdefghij
aswdofflle
bbbbbbbbbb
bisofmlwpa
fsbdfopkld
gikfkwpspa
hogkellgis

可能会回来

abcdefghij
aaadiigjlf
fsbdfopkld
aswdofflle

为了澄清起见,我需要代码来返回在给定位置共享相同字符的任何单词;我心中没有特定的字符(例如示例中给出的“d”和“g”)。另外,我希望它能够返回不符合所有条件的单词;例如,在给出的示例中,单词 1 和 4 共享第四个字符,但不一定是第二个、第七个和第九个。对于我以完成形式运行的程序,我期望它根据九个严格的字符共享标准返回一个非常小的单词列表(可能只有十个)。

编辑:好的,牌摆在桌面上。这就是我如何得到它的问题。

我得到了一个单词列表,并被告知列表中有十个十个字母的单词可以放入网格中,如下所示:

-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 等)都是相同的字母(不同的数字可能是相同的字母,但不一定)。

更新:我仍在运行拉尔夫的代码。现在可能已经完成了,但是在我的外部硬盘驱动器出现故障后,我不得不重新启动该过程。已经快48小时了,但仍然进展缓慢。

答案1

很难避免多次处理文件列表,但每个规则处理一次就足够了。主要处理是对单词进行重复 10 次,同时扩展可能的“单词列表”,其中对于每个列表,第 i:th 单词与该列表的第 i:th 规则相匹配。当每个单词与该列表相应匹配时,就会添加该单词以扩展该列表。

bash对于保留此数据结构来说有点弱,但您可以选择将“单词列表”表示为逗号分隔的单词序列,以指示应用于扩展列表的:R下一个规则。RR当然等于列表中的单词数加 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函数只是测试第一个字符串在给定位置的字符是否与第二个字符串在某个位置的字符匹配。后一个字符串是按逗号分隔顺序排列的先前单词,允许i*11+j第 i:th 单词(从 0 开始)的第 j:th 字符(从 1 开始)的索引方案。例如,索引$((77+2))是第 8 个单词的第二个字符。

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子句。

请注意,这是一个“纯”bash 解决方案,具有 O(N)(或 O(N*T),其中 T 是与第一条规则匹配的数量(如果非常高))。

答案2

我使用示例文本创建了一个单词文件。

-bash-4.2$ cat words
aaadiigjlf
abcdefghij
aswdofflle
bbbbbbbbbb
bisofmlwpa
fsbdfopkld
gikfkwpspa
hogkellgis

该脚本每次都会迭代设置第一个单词的单词列表,然后迭代单词文件的内容并比较第 4 个和第 7 个字符。当它找到匹配时,它会将匹配设置为第二个单词并回显到目前为止的解决方案。该脚本是一个模板,您需要在后续嵌套循环中添加每个附加约束:

-bash-4.2$ cat script
#!/bin/bash

for worda in $(cat ./words ); do
        firstword=$worda
        for wordb in $(cat ./words | grep -v $firstword); do
                if [ $(echo $firstword | cut -c 4,7) = $(echo $wordb | cut -c 4,7) ]; then
                        secondword=$wordb
                        echo "$firstword  $secondword"
                fi
        done
done

这是脚本的输出:

bash-4.2$ ./script
aaadiigjlf  abcdefghij
abcdefghij  aaadiigjlf

提示:尝试将两次出现的 4,7 更改为 4,9,看看这对输出有何影响。您可以尝试嵌套额外的 for 循环。

我不想为你做这一切(因为这似乎是家庭作业)但这应该足以让你走上正确的轨道。您可以从这里使用我给您的内容手动完成此操作,并将每个约束插入比较中。

相关内容