Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP too slow, can anyone see a way to make it faster?

Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let’s say the phone catalogue listed these numbers:

Emergency 911 Alice 97 625 999 Bob 91 12 54 26

In this case, it’s not possible to call Bob, because the central would direct your call to the emergency line as soon as you had dialled the first three digits of Bob’s phone number. So this list would not be consistent.

Input

The first line of input gives a single integer, 1≤t≤40, the number of test cases. Each test case starts with n, the number of phone numbers, on a separate line, 1≤n≤10000. Then follows n lines with one unique phone number on each line. A phone number is a sequence of at most ten digits.

Output

For each test case, output “YES” if the list is consistent, or “NO” otherwise.


The program is supposed to read fron standard in, and write to standard out. We can also assume that the input will adhere to the specification. This is my code:

<?php

    fscanf(STDIN, "%d", $numOfTestCases);

    for ($i = 0; $i < $numOfTestCases; $i++) { //Loop for reading test cases
        fscanf(STDIN, "%d", $numOfPhoneNumbers);

        $phoneNumbers = array();
        $isConsistent = true;

        for ($j = 0; $j < $numOfPhoneNumbers; $j++) { //Loop for reading phone numbers of each test case
            fscanf(STDIN, "%d", $newNumber);

            if ($isConsistent != false) { //If list already inconsistent, we dont need to check further input
                if (empty($phoneNumbers)) { // If the array of phone numbers is empty, we just add the new one
                    $phoneNumbers[$j] = $newNumber;
                } else {
                    foreach ($phoneNumbers as $k => $testNumber) { //Loop for checking if new number is consistent or not
                        $newNumLen = strlen($newNumber);
                        $testNumlen = strlen($testNumber);

                        $newBeginning = substr($newNumber, 0, $testNumlen);
                        $testBeginning = substr($testNumber, 0, $newNumLen);

                        if ($newNumber == $testBeginning || $testNumber == $newBeginning) {
                            $isConsistent = false;
                            break;
                        }
                    }

                    if ($isConsistent == true) $phoneNumbers[$j] = $newNumber;
                }                   
            } 
        }

        $newAnswer = ($isConsistent) ? "YES" : "NO";
        $ansString = ($i == 0) ? $newAnswer : $ansString."\n".$newAnswer;
    }

    fwrite(STDOUT, $ansString);

    exit(0);
?>

My problem is that there is a test program that is running this, which has a timeout of 4 seconds. The second test file always times out. I don't have access to the test program or files but I assume that the file is very long, maybe even 40 test cases with 10000 phone numbers in each case.

Can anyone see how I could make this code run faster in any way?

Here is a sample run:

Sample Input:

2
3
911
97625999
91125426
5
113
12340
123440
12345
98346

Sample Output:

NO
YES
like image 613
BluePrint Avatar asked Mar 26 '15 10:03

BluePrint


1 Answers

As all mention, tree is your solution. create a tree which each node represent a digit in a number. The last digit in each number will mark isNumber=true, and the rest of digit will mark as isNumber=false.

When you add number to the tree, traverse the tree's node and check if you traverse a node with isNumber=true. if so, return false/print something.

E.g: see the ilustration below

add a number 21: iterate on digits. create new node=1, new node=2(isNumber=true)

add number 911: iterate on digits. create new node=9, new node=1, new node=1(isNumber=true) add number 9125: iterate on digits. create new node=9, new node=1, new node=2, new node=5(isNumber=true)

add number 9112: iterate on digits. create new node=9, new node=1, new node=1(ERROR: failed since isNumber=true) enter image description here

hope it help a bit.

EDIT:

Ok, since it interest me, i tried to implement the solution, and i came quite close to yours.

I have made a simple script to create a testcase.txt, containing 10000 numbers( 1≤n≤10000).

I have made a simple unittest.php script that runs 40 times(1≤t≤40), on the same file.

<?php 

// create_testcase.php
$handle = fopen("testcase.txt",'w');

for($i=0 ; $i < 10000 ; $i++){
    $number = rand(1,9999999999);
    fwrite($handle,$number."\n");
}
fclose($handle);

// unittest.php
class Node{
    private $digit;
    private $leaf = false;
    private $children = array();
    function __construct($digit,$leaf = false){
        $this->digit = $digit;
        $this->leaf = $leaf;
    }

    function isLeaf(){
        return $this->leaf;
    }

    function hasChild($digit){
        return array_key_exists($digit,$this->children);

    }

    function hasChildren(){
        return count($this->children);
    }

    function addChild($digit,$isLeaf){
        $this->children[$digit] = new Node($digit,$isLeaf);
        return $this->children[$digit];
    }

    function getChild($digit){
        return $this->children[$digit];
    }

}


for($i=0 ; $i < 40 ; $i++){

    $anchor = new Node(0,false);
    $isConsistent = true;
    $handle = fopen("testcase.txt",'r');

    while(($number = fgets($handle)) != false){
        $node = $anchor;
        $number = rtrim($number);
        $number_length = strlen($number);
        foreach(str_split($number) as $index => $digit){
            if($node->hasChild($digit)){
                $node = $node->getChild($digit);
                if($node->isLeaf()){
                    $isConsistent = false;
                    break;
                }
            }
            else{
                (($index+1) == $number_length) ? ($isLeaf = true) : ($isLeaf = false);
                $node = $node->addChild($digit,$isLeaf);
            }   
        }

        if($node->hasChildren()){
            $isConsistent = false;      
        }

        if(!$isConsistent){
            break;                  // don't continue to next number in test case
        }
    }
    if($isConsistent){
        print "Consist list\n";

    }
    else{
        print "Not Consist list\n";
    }
    fclose($handle);

}

The result took, for my old and still working, core2duo, 2GB memory:
real 0m26.123s user 0m26.064s sys 0m0.048s

for: 40x10000 = 400,000 numbers.

for a single testcase it took: real 0m0.554s user 0m0.544s sys 0m0.008s

like image 57
YyYo Avatar answered Oct 26 '22 00:10

YyYo