Since you're dealing with references, you can traverse your hash with a pointer-type of reference.
my %hash;
while (<STDIN>) {
chomp;
my $pointer = \%hash; # start from root
while (s/^(...)//) { # keep eating 1st 3
if ($_) { # if there's more to go,
# we go deeper
$pointer->{$1} = {} unless ref($pointer->{$1});
$pointer = $pointer->{$1};
} else {
$pointer->{$1}++; # otherwise, set it to 1
}
}
}
Note that the following data will mess things up slightly:
111222333444555
111222333
$HASH{111}->{222}->{333} == 1 # lost 444 and 555
111222333
111222333444555
$HASH{111}->{222}->{333} != 1 # now a hash reference
These are unavoidable though with the criteria you've given. This code also assumes that your string will always be of a length that is divisible by 3. If you have two trailing characters at the end, the substitution will fail, but the test for
$_ will have succeeded in the previous loop, giving you a dangling empty hash reference instead of 1.