加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Perl KMP 算法

发布时间:2020-12-16 00:21:26 所属栏目:大数据 来源:网络整理
导读:了解了一下KMP 算法,自己尝试实现,也比较简单,具体原理参考google baidu,不再重复,这里只作为一个学习的纪录。 use Data::Dumper;my $from = 'abababd ababc';my $find = 'ababc';# 分隔字符串为arraymy @from = split '',$from;my @find = split '',$f

了解了一下KMP 算法,自己尝试实现,也比较简单,具体原理参考google baidu,不再重复,这里只作为一个学习的纪录。

use Data::Dumper;

my $from = 'abababd ababc';
my $find = 'ababc';

# 分隔字符串为array
my @from = split '',$from;
my @find = split '',$find;

print $find,"n";
print @{ calc( @find ) },"n";
print "n";
print kmp( @from,@find );

############################
# 计算字符串的重复值
sub calc {
    my ($array) = @_;

    # 取与字串相同长度的array 并将第一个置0
    my @tmp = (@$array);
    $tmp[0] = 0;

# 之后的每一个字符都与开头字串比较,取@tmp 前一个值作为比较字符
# 若为0,前一字符个与开头不重复,比较字符指向开头0
# 若不为0,前一个字符与开头有重复,如果还相同,@tmp 值加1,否则为0,后继自符重新从头开始比较
    for ( 1 .. $#tmp ) {
        if ( $array->[$_] eq $array->[ $tmp[ $_ - 1 ] ] ) {
            $tmp[$_] = $tmp[ $_ - 1 ] + 1;
        }
        else {
            $tmp[$_] = 0;
        }
    }
    return @tmp;
}

sub kmp {
    my ( $from,$find ) = @_;

    # 初始从开头比较
    my $i   = 0;
    my $tmp = calc($find);

    # 剩余字串长度小于搜索字串,则退出返回-1
    while ( ( $#{$from} - $i ) >= $#{$find} ) {

        # 观察每次步进长度
        print @$from[ $i .. $#{$from} ],"n";

        # j 标记搜索到的字串长度
        my $j = 0;
        while ( $find->[$j] eq $from->[ $i + $j ] ) {

            # 搜索到的长度与字符串长度相同,说明找到,返回index位置
            if ( $j == $#{$find} ) {
                return $i;
            }
            else {
                $j++;
            }
        }

        # 有相同的字符串,根据计算的值来跳转多个字符并重新比较
        # $j 为相同的个数(以1为基数),@tmp[$j-1] 为同开始字符重复的个数
        if ($j) {
            $i += $j - $tmp->[ $j - 1 ];
        }
        else {
            $i++;
        }
    }
    return -1;
}

输出:
#字符串以及计算重复值结果
ababc
00120

#每次查找过程
abababd ababc
ababd ababc
abd ababc
d ababc
 ababc
ababc
8

优化:

由于KMP 搜索过程中已经判断了子字符串的重复性,下一次判断过程中可以根据计算的重复值跳过部分开头字符,直接从不重复的地方开始比较。如下为改进的算法:

sub kmp {
    my ( $from,$find ) = @_;

    # 初始从开头比较
    my $i   = 0;
    my $tmp = calc($find);

    my $j = 0;

    # 剩余字串长度小于搜索字串,则退出返回-1
    while ( ( $#{$from} - $i ) >= $#{$find} ) {

        # 观察每次步进长度
        print "$i $j => ",@$from[ $i .. $#{$from} ],"n";

        # j 标记搜索到的字串长度
        while ( $j <= $#{$find} && $find->[$j] eq $from->[ $i + $j ] ) {
            $j++;
            print ".n"; # 记录由于$j 的变化自符比较的减少过程,用于debug
        }

        # 有相同的字符串,根据计算的值来跳转多个字符并重新比较
        # $j 为相同的个数(以1为基数),@tmp[$j-1] 为同开始字符重复的个数
        if ( !$j ) {
            $i += 1;
        }
        elsif ( $j <= $#{$find} ) {
            $i += $j - $tmp->[ $j - 1 ];
            $j = $tmp->[ $j - 1 ];
        }
        else {
            return $i;
        }
    }
    return -1;
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读