#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use constant MIN_LENGTH => 2; use constant MIN_REPEATS => 2; use Benchmark qw/:all :hireswallclock/; my $str='aabcdabcabcecdecd'; sub blazar { local $_=shift; my $l=length; my %count; for my $off (0..$l-1) { for my $len (MIN_LENGTH .. $l-$off) { my $s = substr $_, $off, $len; $count{ $s } ||= ()= /$s/g; } $count{$_} < MIN_REPEATS and delete $count{$_} for keys %count; } \%count; } sub oha { my $s=shift; my %saw; while($s =~ /(..+)(?=.*?\1)/g) { for my $x (0..length $1) { @saw{ map {substr $1, $x, $_} $x+2..length $1 } = (); } } $saw{$_} =()= $s =~ /\Q$_/g for keys %saw; \%saw; } sub ikegami { my $str = shift; local our %counts; $str =~ / (.{2,}) # or (.+) (?(?{ !$counts{$1} })(?=.*\1)) (?{ ++$counts{$1} }) (?!) /x; \%counts; } sub lodin { my $str = shift; local our %count; $str =~ / (.{2,}) (?(?{ $count{$1} }) (?!) ) .* \1 (?{ ($count{$1} ||= 1)++ }) (?!) /x; \%count; } { my %count; sub kramba { my( $string) = @_; my $length = length( $string ); if ($length < MIN_LENGTH) { for (keys %count) { delete $count{$_} if $count{$_} < MIN_REPEATS; } return \%count; } for my $l (MIN_LENGTH..$length) { my $s = substr( $string, 0, $l ); $count{ $s } += 1; } kramba( substr( $string, 1 ) ); }; } for my $multiplier (1, 2, 4) { my $work_str = "$str " x $multiplier; print "Results for string:\n\n\"$work_str\"\n\n"; cmpthese 2000/$multiplier => { blazar => sub { blazar $work_str }, oha => sub { oha $work_str }, kramba => sub { kramba $work_str }, ikegami => sub { ikegami $work_str }, lodin => sub { lodin $work_str }, } }