开发者

Speed problem with SPOJ occurence counting in perl

I'm having a problem with a task similar to this one: click (translated) (the one I was assigned with has way bigger tests and a lower time limit). A quick translation of the task:

Write a program that checks how many times the given number occurred in a given sequence.

Input: Given number, how many numbers are in the sequence, the sequence of numbers

Output: The number of occurrences

My solutions so far:

1:

#!/usr/bin/env perl

while (<>) {
    $in = $_;
    @nums = split / /, $in, 3;

    $what = shift @nums;
    shift @nums;
    $rest = shift @nums;
    $rest = " ".$rest." ";

    $sum = () = $rest =~ /(?<=\s)$what(?=\s)/g;

    print $sum;
    print "\n";
}

2:

#!/usr/bin/env perl

while (<>) {
    $in = $_;
    @nums = split / /, $in, 3;

    $what = shift @nums;
    shift @nums;
    $rest = shift @nums;
    $rest = " ".$rest." ";开发者_运维百科

    if(!$reg{$what}){
        $reg{$what} = qr/(?<=\s)$what(?=\s)/;
    }
    $sum = () = $rest =~ /$reg{$what}/g;

    print $sum;
    print "\n";
}

I also tried the brute force approach, hash tables, grep... All exceed the given time limit, and I've got no idea how to write anything that will work faster than the above two. Any ideas?

edit: After getting rid of copying lists (turns out the numbers can also be negative):

#!/usr/bin/env perl

while ($line = <>) {
        $line =~ s/^(-?\d+) \d+//;
        $what = $1;

        $sum = () = $line =~ / $what\b/g;

    print $sum;
    print "\n";
}

edit2: via http://www.chengfu.net/2005/10/count-occurrences-perl/:

print $sum = (($line =~ s/ $1\b//g)+0);

resulted in 2x faster code than:

print $sum = () = $line =~ / $1\b/g;

Works now, thanks :)


For one thing, you're doing an awful lot of copying. I've marked each time you copy a large string in your first example:

while (<>) {
    $in = $_;                   # COPY
    @nums = split / /, $in, 3;  # COPY

    $what = shift @nums;
    shift @nums;
    $rest = shift @nums;        # COPY
    $rest = " ".$rest." ";      # COPY

    $sum = () = $rest =~ /(?<=\s)$what(?=\s)/g;

    print $sum;
    print "\n";
}

To speed things up, avoid the copies. For example, use while ($in = <>) (or just skip $in and use $_).

For extracting $what and the count, I think I'd try this instead of split:

$in =~ s/^(\d+) \d+//;
$what = $1;

Instead of adding a space fore and aft, just use \b instead of lookarounds with \s.

    $sum = () = $in =~ /\b$what\b/g;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜