use strict; use warnings; use Data::Dumper; my $str1 = 'Perlmonks is the best perl community'; my $str2 = 'Perlmonks is one of the best community of perl users'; if ($str1 eq $str2) { print $str1; exit; } my @wl1 = split /\s+/, $str1; my @wl2 = split /\s+/, $str2; my $wp1 = build_word_hash(\@wl1); my $wp2 = build_word_hash(\@wl2); my $diff_str1 = ''; my $diff_str2 = ''; while (@wl1 || @wl2) { my $word1 = shift @wl1; my $word2 = shift @wl2; if ($word1 && $word2 && $word1 eq $word2) { $diff_str1 .= $word1 . ' '; # pairing the word from the origional string with it's output $diff_str2 .= $word2 . ' '; # lets us do things like case insensitive, but preserving match later shift @{$wp1->{$word1}}; # eat this word shift @{$wp2->{$word2}}; # eat this word next; } #process word1 first, for fun if ($word1) { if ($wp2->{$word1} && @{$wp2->{$word1}} && ! grep {$_ == $wp2->{$word1}->[0]} @{$wp1->{$word1}} ) { # word moved. # the grep checks that the next occurance of the word in string 2 ($wp2->{$word}->[0] # does not also have an occurance of the word in string 1. # if it does not, it means that this is a move of the word. $diff_str1 .= "[$word1] "; shift @{$wp2->{$word1}}; # eat this word } else { # Easy case, word in string 1 but not string 2 $diff_str1 .= "<$word1> "; } } if ($word2) { if ($wp1->{$word2} && @{$wp1->{$word2}} && ! grep {$_ == $wp1->{$word2}->[0]} @{$wp2->{$word2}} ) { $diff_str2 .= "[$word2] "; shift @{$wp1->{$word2}}; # eat this word } else { $diff_str2 .= "<$word2> "; } } } print "$diff_str1\n$diff_str2\n"; sub build_word_hash { my $wl = shift; my $res = {}; my $i = 0; foreach my $word( @$wl ) { push @{$res->{$word}} , $i++; } return $res; } #### Perlmonks is [the] [best] [perl] [community] Perlmonks is [the] [best] [community] [perl]