请求shell脚本来定位每行输入的最长重复段

时间:2014-04-26 03:55:01

标签: shell awk sed

我有一个大文件,其中每行包含由空格分隔的24个小整数。我想为每一行找到重复的最长段,允许行到wrap around。例如,给定行

0 10 4 2 7 9 11 8 6 5 0 10 4 2 7 11 9 3 8 3 1 1 6 5

序列6 5 0 10 4 2 7最长;它的长度为7,两个匹配项由10个位置(或14)分隔。

有人可以告诉我如何将一个脚本拼凑起来,为每一行返回最长序列的长度和两个开头之间的间隔吗?

构建文件的方式不可能重复任何一个段(即两次以上),因为从011的每个数字都被限制为完全出现两次。

非常感谢。 --Lloyd

2 个答案:

答案 0 :(得分:0)

有很多语言可以让这比awk(包括gawk)更容易,但这里有一个全部的答案。

尝试将其放入可执行的awk文件中:

#!/usr/bin/awk -f

BEGIN { DELIM=":" }

function reorder(start) {
    head = ""
    tail = ""
    for( i=1;i<=NF;i++ ) {
        if( i<start ) tail = sprintf( "%s%s%s", tail, $i, FS )
        else head = sprintf( "%s%s%s", head, $i, FS )
    }

    # last field is the starting index
    return( head tail start )
}

function longest(pair) {
    split( pair, a, DELIM )
    split( a[1], one, FS )
    split( a[2], two, FS )
    long = ""
    for( i=1;i<=NF;i++ ) {
        if( one[i] != two[i] ) break
        long = sprintf( "%s%s%s", long, one[i], FS )
    }
    return( i-1 DELIM two[NF+1]-one[NF+1] DELIM long )
}

{
for( k=1;k<=NF;k++ ) {
    pairs[$k] = (pairs[$k]==""?"":pairs[$k]DELIM) reorder( k )
    }
for( p in pairs ) {
    tmp = longest( pairs[p] )
    out = tmp>out ? tmp : out
    }
print out
}

如果我调用此awko,则运行awko data会以以下格式生成数据:

# of matched fields:index separation:longest match

对于输入数据是:

7:14:6 5 0 10 4 2 7 

请注意,我并不打算清理匹配数据末尾的额外空间。有了更多的输入数据,如果有错误,我会有更好的想法。


我想知道我能做到这么快:

#!/usr/bin/awk -f

BEGIN { OFS=":" }

function longest(first, second) {
    long = ""
    s = second
    flds = 0
    for( f=first;f<=NF;f++ ) {
        if( $f != $s ) break
        long = sprintf( "%s%s%s", long, $f, " " )
        if( s==NF ) s=0
        s++
        flds++
    }
    return( flds OFS second-first OFS long )
}

{
for(k=1;k<=NF;k++) {
    val = pos[$k]
    if( val!="" ) {
        tmp = longest( val, k )
        delete pos[$k] #### need an awk/gawk that can remove elems or "delete pos" l8r
        }
    else pos[$k] = k
    out = tmp>out ? tmp : out
    }
print out
}

它比第一轮快200%。它只使用一个外部字段循环并处理每个匹配的数字,因为第二个是使用原始解析字段找到的。一遍又一遍地运行相同的数据(2400行值)给了我0.33s的系统时间,而不是我从同一数据的第一个脚本得到的71.10s。

答案 1 :(得分:0)

这是一个相当混淆的解决方案,适用于单行输入。将整个事物包裹在一个循环中,从你的输入中读取行而不是明确地设置它,你应该有一个可行的(虽然非常缓慢和丑陋)解决方案。

#!/bin/sh

input='0 10 4 2 7 9 11 8 6 5 0 10 4 2 7 11 9 3 8 3 1 1 6 5'

trap 'rm -f $TMP1 $TMP2' 0
TMP1=$(mktemp $(basename $0.XXXX))
TMP2=$(mktemp $(basename $0.XXXX))

input="$input $input"  # handle wrap-around
seq 0 11 | while read start_value; do
    echo $input | tr \  \\n | grep -w -n $start_value | sed 's/:.*//' | {
        read i
        read j
        delta=$( expr $j - $i )
        echo $input | tr \  \\n | sed -n "$i,${j}p" > $TMP1
        echo $input | tr \  \\n | sed -n "$j,\$p" > $TMP2
        diff $TMP1 $TMP2 | { IFS=a read length junk
            echo $length $delta $start_value
        }
    }
done | sort -rn | sed 1q | { read length delta start;
    printf "%s " "The sequence"
    echo $input | tr \  \\n | awk '$0==k{t=1}t' k=$start | sed "${length}q"
    echo ' is the longest sequence.' 
    /bin/echo -n The difference between starting positions is $delta '(or ' 
    expr 24 - $delta
    echo ')'
} | tr \\n ' '
echo