【问题标题】:Edit distance: Ignore start/end [closed]编辑距离:忽略开始/结束[关闭]
【发布时间】:2017-07-22 12:43:02
【问题描述】:

我正在寻找一种可以编辑距离的算法,但它会忽略一个字符串和空格中的 start+end:

edit("four","foor") = 1
edit("four","noise fo or blur") = 1

是否有现有的算法?甚至可能是 Perl 或 Python 库?

【问题讨论】:

  • 你在谈论莱文斯坦吗,在这里查看en.wikibooks.org/wiki/Algorithm_Implementation/Strings/…
  • 堆栈溢出不是代码编写服务。
  • 再次反对投票的“机器”。 OP 不要求提供代码 - 所以,@ppperry 的评论放错了地方。这里允许关于“算法”的问题,因为它显然符合范围。伙计们,真的,请在投反对票之前三思。如果符合范围,没有代码的问题都可以。

标签: python algorithm perl fuzzy-search


【解决方案1】:

执行此操作的代码在概念上很简单。您可以自行添加您想要忽略的内容:

#!perl
use v5.22;
use feature qw(signatures);
no warnings qw(experimental::signatures);

use Text::Levenshtein qw(distance);

say edit( "four", "foor" );
say edit( "four", "noise fo or blur" );

sub edit ( $start, $target ) {
    # transform strings to ignore what you want
    # ...
    distance( $start, $target )
    }

也许你想检查所有相同长度的子字符串:

use v5.22;
use feature qw(signatures);
no warnings qw(experimental::signatures);

use Text::Levenshtein qw(distance);

say edit( "four", "foar" );
say edit( "four", "noise fo or blur" );

sub edit ( $start, $target ) {
    my $start_length = length $start;
    $target =~ s/\s+//g;
    my @all_n_chars = map {
        substr $target, $_, 4
        } 0 .. ( length($target) - $start_length );

    my $closest;
    my $closest_distance = $start_length + 1;
    foreach ( @all_n_chars ) {
        my $distance = distance( $start, $_ );
        if( $distance < $closest_distance ) {
            $closest = $_;
            $closest_distance = $distance;
            say "closest: $closest Distance: $distance";
            last if $distance == 0;
            }
        }

    return $closest_distance;
    }

这个非常简单的实现可以找到您想要的。但是,请注意,其他随机字符串可能会意外地具有较小的编辑距离。

closest: foar Distance: 1
1
closest: nois Distance: 3
closest: foor Distance: 1
1

您可以扩展它以记住每个字符串的真实起始位置,以便您可以在原始字符串中再次找到它,但这应该足以让您继续前进。如果您想使用 Python,我认为该程序可能看起来非常相似。

【讨论】:

    【解决方案2】:

    这是一个 Perl 6 解决方案。尽管有插页式的东西,我使用的语法知道如何抓取四个有趣的字符。更复杂的要求需要不同的语法,但这并不难。

    每次匹配时,NString::Actions 类对象都会更改以检查匹配。它和我之前做的一样高水位标记。这看起来像是更多的工作,它是为了这个简单的例子。对于更复杂的示例,情况不会更糟。我的 Perl 5 版本必须使用大量工具来确定要保留或不保留的内容。

    use Text::Levenshtein;
    
    my $string = 'The quixotic purple and jasmine butterfly flew over the quick zany dog';
    
    grammar NString {
        regex n-chars      { [<.ignore-chars>* \w]**4 }
        regex ignore-chars { \s }
        }
    
    class NString::Actions {
        # See 
        my subset IntInf where Int:D | Inf;
    
        has        $.target;
        has Str    $.closest          is rw = '';
        has IntInf $.closest-distance is rw = Inf;
    
        method n-chars ($/) {
            my $string = $/.subst: /\s+/, '', :g;
    
            my $distance = distance( $string,  self.target );
            # say "Matched <$/>. Distance for $string is $distance";
            if $distance < self.closest-distance {
                self.closest = $string;
                self.closest-distance = $distance;
                }
            }
        }
    
    my $action =  NString::Actions.new: target => 'Perl';
    
    loop {
        state $from = 0;
        my $match = NString.subparse(
            $string,
            :rule('n-chars'),
            :actions($action),
            :c($from)
            );
        last unless ?$match;
    
        $from++;
        }
    
    say "Shortest is { $action.closest } with { $action.closest-distance }";
    

    (我从 Perl 5 做了一个直接移植,我将在这里留下)

    我在 Perl 6 中尝试过同样的事情,但我确信这有点冗长。我想知道是否有一种聪明的方法可以抓取 N 个字符组进行比较。也许我以后会有一些改进。

    use Text::Levenshtein;
    
    put edit( "four", "foar" );
    put edit( "four", "noise fo or blur" );
    
    sub edit ( Str:D $start, Str:D $target --> Int:D ) {
        my $target-modified = $target.subst: rx/\s+/, '',  :g;
    
        my $last-position-to-check = [-] map { .chars }, $target-modified, $start;
    
        my $closest = Any;
        my $closest-distance = $start.chars + 1;
        for 0..$last-position-to-check -> $starting-pos {
            my $substr = $target-modified.substr: $starting-pos, $start.chars;
            my $this-distance = distance( $start, $substr );
            put "So far: $substr -> $this-distance";
            if $this-distance < $closest-distance {
                $closest          = $substr;
                $closest-distance = $this-distance;
                }
            last if $this-distance = 0;
            }
    
        return $closest-distance // -1;
        }
    

    【讨论】:

      猜你喜欢
      • 2014-11-29
      • 2010-11-07
      • 1970-01-01
      • 2010-09-25
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多