File Coverage

blib/lib/String/CyclicRotation.pm
Criterion Covered Total %
statement 55 55 100.0
branch 15 16 93.7
condition 6 6 100.0
subroutine 8 8 100.0
pod 1 1 100.0
total 85 86 98.8


line stmt bran cond sub pod time code
1             package String::CyclicRotation;
2              
3 1     1   28630 use strict;
  1         2  
  1         38  
4 1     1   5 use warnings;
  1         1  
  1         29  
5              
6 1     1   5 use Exporter;
  1         7  
  1         39  
7 1     1   5 use Carp;
  1         1  
  1         1539  
8              
9             our @ISA = qw(Exporter);
10             our @EXPORT = qw();
11             our %EXPORT_TAGS = ( 'all' => [ qw(is_rotation) ] );
12             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'}} );
13              
14             our $VERSION = '0.01';
15              
16              
17             =head1 NAME
18              
19             String::CyclicRotation - Checks if a string is a cyclic rotation of another string.
20              
21             =head1 SYNOPSIS
22              
23             use String::CyclicRotation qw(is_rotation);
24             my $res = is_rotation("table", "ablet"); #true
25              
26             =head1 DESCRIPTION
27              
28             Checks if a string is a cyclic rotation of another string. This test is done in O(n).
29              
30             =cut
31              
32             =head1 METHODS
33              
34             =head2 is_rotation
35              
36             Checks if a string is a cyclic rotation of another string.
37              
38             =cut
39              
40             sub is_rotation {
41 32 50   32 1 4459 croak "Incorrect number of parameters." if @_ != 2;
42              
43 32         46 my ($str1, $str2) = @_;
44 32         27 my @res;
45              
46             #trivial cases
47 32 100       66 return 0 if length $str1 != length $str2;
48 28 100       45 return 1 if ! length $str1;
49              
50 26         69 _compute_z($str1 . $str2, \@res);
51              
52 26         75 return _is_rotation($str1 . $str2, \@res);
53             }
54              
55             sub _is_rotation {
56 26     26   33 my ($str, $res) = @_;
57 26         26 my $i;
58              
59 26         41 for my $k (length($str)/2..$#{$res}) {
  26         55  
60 75         154 do {
61 26         24 $i = $k;
62 26         36 last;
63 75 100       65 } if $res->[$k] == ($#{$res} - $k);
64             }
65              
66 26         57 my $str1 = substr($str, (length($str) / 2), (length($str) / 2) - $res->[$i]);
67 26         37 my $str2 = substr($str, $res->[$i], (length($str) / 2) - $res->[$i]);
68 26 100 100     137 return 1 if $str1 eq $str2 || $str1 eq reverse $str2;
69 8         29 return 0;
70             }
71              
72             sub _compare {
73 176     176   186 my ($str, $left, $right) = @_;
74 176         163 my $length = 0;
75              
76 176   100     637 while ($right < length $str && (substr $str, $left, 1) eq (substr $str, $right, 1)) {
77 91         78 $length++;
78 91         72 $left++;
79 91         298 $right++;
80             }
81 176         246 $length;
82             }
83              
84             sub _compute_z {
85 26     26   35 my ($str, $res) = @_;
86 26         31 my ($k, $r, $l) = (0, -1, 0);
87              
88 26         46 for my $k (1..length $str) {
89 200 100       284 if ($k > $r) {
90 156         205 my $length = _compare ($str, 0, $k);
91              
92 156         211 $res->[$k] = $length;
93 156 100       326 if ($res->[$k] > 0) {
94 47         57 $r = $k + $res->[$k] - 1; # update right side of v-box
95 47         94 $l = $k; # update left side of v-box
96             }
97             }
98             else {
99 44         71 my ($z2, $beta) = ($res->[$k - $l], $r - $k + 1);
100              
101 44 100       55 if ($z2 < $beta) {
102 24         40 $res->[$k] = $z2;
103             }
104              
105             else {
106             # $s->[$k..$r] is a prefix of $$s
107 20         32 my $length = _compare ($str, $r + 1, $beta);
108 20         33 my $pos = $length + $r + 1; # position of mismatch
109 20         27 $res->[$k] = $pos - $k;
110 20         19 $r = $pos - 1;
111 20         27 $l = $k;
112             }
113             }
114             }
115             }
116              
117             1;
118              
119             __END__