0plus1 Blog

Facebook Hacker Cup 2011 Qualification – Studious Student

by admin on Jan.09, 2011, under Uncategorized

Here we go with another problem. Here’s the test

Studious Student

You’ve been given a list of words to study and memorize. Being a diligent student of language and the arts, you’ve decided to not study them at all and instead make up pointless games based on them. One game you’ve come up with is to see how you can concatenate the words to generate the lexicographically lowest possible string.

Input

As input for playing this game you will receive a text file containing an integer N, the number of word sets you need to play your game against. This will be followed by N word sets, each starting with an integer M, the number of words in the set, followed by M words. All tokens in the input will be separated by some whitespace and, aside from N and M, will consist entirely of lowercase letters.

Output

Your submission should contain the lexicographically shortest strings for each corresponding word set, one per line and in order.

Constraints

1 <= N <= 100
1 <= M <= 9
1 <= all word lengths <= 10

Basically we have to find the composed string that is the lexicographically‘s shortest. So where is the issue here? The issue is that the problem states that you have to find the shortest possible composed string, so order each of the words cannot be simply ordered and combined because that may or may not be the correct solution (we will go there later). The easiest way to do this is to recursively test each of the possible permutations of the words against each other, this is the bruteforcing method. Let’s see.

Bruteforcing Method

First we have to parse the input:

function fromTxtToArray($filename){
    $fh = fopen($filename, "rb");
    $data = fread($fh, filesize($filename));
    fclose($fh);

    $array = explode("\n",$data);
    $rows = $array[0];
    unset($array[0]);

	$i = 0;
	$return = array();
	foreach ($array as $k => $v){
		$return[$i] = explode(' ',trim($v));
		unset($return[$i][0]);
		$i++;
	}
    return array('rows' => $rows, 'data' => $return);
}

Then we have to permutate each of the words we have been given and then sort them (php sort luckily automagically sorts strings lexicographically) returning the first one.

//source of this function -> http://docstore.mik.ua/orelly/webprog/pcook/ch04_26.htm
function permute($items, $perms = array()) {

	global $permutations;

    if (empty($items)) {
        $permutations[] = join('', $perms);
    }  else {
		for ($i = count($items)-1; $i >= 0; --$i) {
		     $newitems = $items;
             $newperms = $perms;
             list($foo) = array_splice($newitems, $i, 1);
             array_unshift($newperms, $foo);
             permute($newitems, $newperms);
         }
		 return $permutations;
    }
}

function sortAndKill($array){
	sort($array,SORT_STRING);
	return $array[0];
}

$data = fromTxtToArray('input.txt');

set_time_limit(0);

foreach ($data['data'] as $k => $v){
	$permutations = array();
   	$combinations = permute($v);
	echo sortAndKill($combinations)."\n";
	unset($permutations);
}

This works perfectly with the example, sadly with the real output it will take too long (I let it run for more than 20 minutes with no result). If you wanna test it just copy and paste the test input into a txt file.

5
6 facebook hacker cup for studious students
5 k duz q rc lvraw
5 mybea zdr yubx xe dyroiy
5 jibw ji jp bw jibw
5 uiuy hopji li j dcyi

It’s obvious that we have to switch to dp.

Dynamic Programming

What is the problem here? Is that we have too many cases to test, we have to find a way to exclude obviously wrong cases. We can start by ordering the words we have been given and see the results.

function lexiOrder($array){
	sort($array,SORT_STRING);
    $string = false;
    foreach ($array as $k => $v){
        $string .= trim($v);
    }
    return $string;
}

$data = fromTxtToArray('input.txt');

$result = false;
foreach ($data['data'] as $k => $v){
    $result .= lexiOrder($v)."\n";
}
echo $result;

It works! No wait a minute..

The string: bwjijibwjibwjp is wrong it should be: bwjibwjibwjijp this is because while ji comes before jibw merging them togheter will return jijibw while the correct one is jibwji (b comes before j) the standard sorting will not work anymore.. we have to use usort.

function letterwalk($a,$b){

    if ($a == $b) {
        return 0;
    }
    for ($i=0;$i<10;$i++){

        $lettera = substr($a, $i, 1);
        $letterb = substr($b, $i, 1);

		if ($lettera < $letterb){
			return -1;
		} elseif($lettera > $letterb) {
			return 1;
		}
    }
}

//usort($array, "letterwalk");

So what is this? It’s a function to walk and compare sequentially every single letter of the word against each other. But wait we still get the same result as before! This is because comparing ji agains jibw will fail because b will get compared to null. To avoid this we have to edit the function:

function letterwalk($a,$b){

    if ($a == $b) {
        return 0;
    }
    for ($i=0;$i<10;$i++){

        $lettera = substr($a, $i, 1);
        $letterb = substr($b, $i, 1);

        if($lettera && $letterb) {
            if ($lettera < $letterb){
                return -1;
            } elseif($lettera > $letterb) {
                return 1;
            }
        } else {
            if($lettera==$letterb){
                return 0;
            } elseif ($lettera < $letterb) {
                return 1;
            } else {
                return -1;
            }
        }
    }
}

Finally the example works! But we are not finished yet. The problem is that there may be case where a shorter string must go AFTER the longest one.
to clarify:

ji / jibw

jibwji is the correct answer, so ji must go after jibw.

but

aa / aabb

aaaabb is the correct answer, aa must go before aabb

How do we test this? We can sort them in two different ways! So here we have the final solution:

function lexiOrder($array,$mode){

	if ($mode == 'letter'){
		usort($array, "letterwalk");
	} elseif ($mode == 'word'){
		sort($array,SORT_STRING);
	}

    $string = false;
    foreach ($array as $k => $v){
        $string .= trim($v);
    }
    return $string;
}

$data = fromTxtToArray('input.txt');

$result = false;
foreach ($data['data'] as $k => $v){

	$order_l = lexiOrder($v,'letter');
	$order_w = lexiOrder($v,'word');

	$order_f = array($order_l,$order_w);
	sort($order_f,SORT_STRING);

	$result .= $order_f[0]."\n";
}
echo $result;

BUT!! What about if we find a case with two couples like this:

ji jibw
aa aabb

The correct order would be:

aaaabb jibwji

Since our code now can only test two cases (the shortest before and the shortest after) should we find such a case the algotithm will fail since it will only have:

aaaabb jijibw
aabbaa jibwji

to test, neither of them are correct. We are takling the problem from the wrong perspective. We need to be smart. Let’s analyse the strings.

ji jibw -> jibwji Because the j comes after the b
aa aabb -> aaaabb because the a comes before the b

So..

ji jibw -> jiJ jibw
aa aabb -> aaA aabb

We also know that words are between 1 and 10 chars so we can safely pad them to

ji jibw -> jiJJJJJJJJ jibwJJJJJJ
aa aabb -> aaAAAAAAAA aabb
AAAAAA

aaaabb jijibw
aabbaa jibwji

This way the strings can be sorted with a simple php sort!

function padAndOrder($array){
	foreach ($array as $k => $v) {
        echo $strings[$k] = str_pad($v, 10 + strlen($v), substr($v,0,1));
    }

	sort($strings, SORT_STRING);

	$return = false;
    foreach ($strings as $k => $v) {
        $return .= substr($v, 0, -10);
    }
	return $return;
}

Once again enjoy! I’m doing this to share my method (and the way I’ve come to it) so that it may inspire other, there are maybe thousand of better ways to do this, it’s just my take. Peace.

Leave a Comment more...

Facebook Hacker Cup 2011 Qualification – Double Squares

by admin on Jan.09, 2011, under Programming

Most of the programmers now about the Hacker Challenge from Facebook. I will now explain my take on it using php.
The reason I’m doing this is because this contest is an epic fail (read here the reasons why) full of bug, with really confused rules badly explained (I’m looking at you 6 minutes time limit with no warning and the code/no-code in the reply question). Anyway it’s still an interesting programming puzzle..

Let us start with the text of the problem:

Double Squares
A double-square number is an integer X which can be expressed as the sum of two perfect squares. For example, 10 is a double-square because 10 = 32 + 12. Your task in this problem is, given X, determine the number of ways in which it can be written as the sum of two squares. For example, 10 can only be written as 32 + 12 (we don’t count 12 + 32 as being different). On the other hand, 25 can be written as 52 + 02 or as 42 + 32.

Input
You should first read an integer N, the number of test cases. The next N lines will contain N values of X.
Constraints
0  <  X  <  2147483647
1  < N  < 100
Output
For each value of X, you should output the number of ways to write X as the sum of two squares.

Facebook right at the top of the page gives you an hint: Too hard for brute force, switching to dp
This is because the problems that have been given are fairly easy to code using brute force, but hard to do the smart way, since we are using php here the smart way is the only way to go (you’ll see the reason in a while).

Let’s start.

First thing is to read the input, easy one.. since I like multidimensional arrays I did it like this:

function fromTxtToArray($filename){
	$fh = fopen($filename, "rb");
	$data = fread($fh, filesize($filename));
	fclose($fh);

	$array = explode("\n",$data);
	$rows = $array[0];
	unset($array[0]);

	return array('rows' => $rows, 'data' => $array);
}

Then we have the main cycle (I discarded completely the rows because I like foreach better than for):

$data = fromTxtToArray('input.txt');
foreach ($data['data'] as $k => $v){
    //?????
}

Now that the main logic is out of the way let’s work the problem, starting with the:

bruteforcing method


function scompose($number){
	$array = array();

	if ($number % 2 == 0) {
		$stop = $number/2;
	} else {
		$stop = round($number/2, 0, PHP_ROUND_HALF_DOWN);
	}

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

		if($i>$stop){
			break;
		}
		$num1 = $number-$i;
		$num2 = $i;

		if(isPerfect($num1)&&isPerfect($num2)){
			$array[] = array('num1'=>$num1,'num2'=>$num2);
		}
	}

	return $array;
}

$data = fromTxtToArray('input.txt');

set_time_limit(0);

foreach ($data['data'] as $k => $v){
     echo count(scompose($v))."\n";
}

Basically what this does is to cycle the number in order to get all of his possible couple of addends, it does this simply by subtracting $i from the number and taking $i as the other addend. Now this will create a real issue because doing so will give you repeated couples (eg: take five you’ll get 0,5;1,4;2,3;3,2;4,1;5,0) this is wrong since the problem text clearly states that there shouldn’t be repetition. To avoid this we have to divide the number by two, rounding him down in case it’s odd, using this value we can break the cycle when we get to that point. After that we check if both of the addends of the numbers are perfect squares if so we put them in an array, when the cycle stops we count the array and we are done. The missing functions are these:

//Avoids problem with the buggy is_int native php function
function betterIs_int($int){

	if(is_numeric($int) === true){
		if((int)$int == $int){
			return true;
		} else {
			return false;
		}
	}else{
		return false;
	}
}
//Check if the square root of a number is an integer (perfect square)
function isPerfect($int){
	if(betterIs_int(sqrt($int))){
		return true;
	} else {
		return false;
	}
}

I actually let this thing run for fun with the input.txt file and I stopped it after 14 minutes! It works with the examples though, just use this:

$data['data']= array('10','25','3','0','1');

Let’s move to the:

Dynamic programming

If you are not acquainted with this methodology please read here and here.

The problem we had with the brute force was that the for cycle was very very loooong, we have to find a better way to approach this! See we are checking every couple to see if they’re perfect square what about a method where we can rule out directly if there are? First we have to find the highest possible square root of the number, since we cannot exceed this number we have less cases to test. Then we cycle $i from 0 to the maxsquare and we check if the square root of the difference between the number and the square of $i is an integer, if it is we have a winning couple! But wait we still have a problem, the couples are once again repeated! We have to divide them by 2.

function firstDraft($number){

    if($number==0||$number==1){
	        return 1;
	}

	$result=0;

	$maxsquare = floor(sqrt($number));

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

		$difference = sqrt($number-($i*$i));

		if (betterIs_int($difference)) {
			$result++;
		}
	}
	return ceil($result/2);
}

Wait! Divide them by two? Why? We could just calculate the half square root of the number and trim down the cases to test! And so..

function dp($number){

    if($number==0||$number==1){
	        return 1;
	}

	$result=0;

	$halfmaxsquare = ceil(sqrt($number/2));

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

		$difference = sqrt($number-($i*$i));

		if (betterIs_int($difference)) {
			$result++;
		}
	}
	return $result;
}

$data = fromTxtToArray('input.txt');

foreach ($data['data'] as $k => $v){
	echo dp($v)."\n";
}

No need to set time limit here :-P
enjoy!

Leave a Comment :, , , more...

Append value to multiselect with jquery

by admin on Aug.12, 2010, under Programming

This is a simple function to append values to a multiselect. In the comments you’ll find the explanation of each command.

function appendMulti(value,target){
    //Get old values from multiselect (they are an array)
	oldvalues= $(target).val();
	//We need to check if the multiselect is empty
	if(oldvalues){
        //It's not empty
		//Find the lenght of the array, the beauty of this is that since arrays starts from 0, this number will always be the next available slot
		key = value.length;
	} else {
        //It's empty
		//Initialize a new array and set the key to the first element
		var oldvalues = [];
		key = 0;
	}
	//Append new value to the array
	oldvalues[key] = value;
	//Set the new values
	$(target).val(oldvalues);

	return true;
}

You can call the function like this:

appendMulti('value','#multiselect');
Leave a Comment :, more...

Ajax calls vs MSSql

by admin on Jun.29, 2010, under Programming

Please note that this article can be applied to any non utf-8 encoding.

I recently needed to send data using ajax to an MSSql 2000 database (which requires windows-1252).
As you may know javascript sends data encoded in utf-8 (utf-16 to be more precise), this must be accepted as a fact thus making the use of a php function to convert the encoding before sending to the database. Here is the snippet I use:

function convertor($array){
	if (is_array($array) && count($array) > 0){
		foreach ($array as $k => $v){
			$return[$k] = mb_convert_encoding($v, "iso-8859-1","UTF-8");
		}
	} elseif (!empty($array)) {
		$return = mb_convert_encoding($array, "iso-8859-1","UTF-8");
	} else {
		return false;
	}
	return $return;
}

Basically what it does is to check if we have an array or a single value then proceed to convert it to the desired encoding (in my case iso-8859-1), after converting the values these can be safely stored into the database.

I have also to add that I’ve encountered some jquery plugins that messes with the encoding of the strings. To be on the safe side you may use this line:

unescape(encodeURIComponent(string))
Leave a Comment :, , , , more...

jquery-ui dialog with ajax how to avoid a common error

by admin on May.31, 2010, under Programming

I’ve made this post because I got asked why this snippet gave problems:

$.ajax({
   type: "POST", url: "somepage.php",
   success: function(html){$("#someDiv").html(html).dialog();}
});

The problem with this code is that it creates a new instance of dialog each time, basically adding a new dialog div in the html for each call.
Aside from being unable to reopen the dialog after closing it, your are replicating your ajax code several times across the webpage creating all sort of problems with the jquery selectors.

The jquery-ui docs do not expalin well this issue, so to avoid headache here is the correct code:

$(function() {
   $("#someDiv").dialog({ autoOpen: false });
});

then in the ajax call:

$.ajax({
   type: "POST", url: "somepage.php",
   success: function(html){$("#someDiv").html(html).dialog('open');}
});
Leave a Comment :, , more...

European Countries array

by admin on Jun.03, 2009, under Programming

We recently had to create a select reserved only to contries from the european union, here’s the array with the standard countries codes:

$eu_countries = array(
  "AT" => "Austria",
  "BE" => "Belgium",
  "BG" => "Bulgaria",
  "CY" => "Cyprus",
  "CZ" => "Czech Republic",
  "DK" => "Denmark",
  "EE" => "Estonia",
  "FI" => "Finland",
  "FR" => "France",
  "DE" => "Germany",
  "GR" => "Greece",
  "HU" => "Hungary",
  "IE" => "Ireland",
  "IT" => "Italy",
  "LV" => "Latvia",
  "LT" => "Lithuania",
  "LU" => "Luxembourg",
  "MT" => "Malta",
  "NL" => "Netherlands",
  "PL" => "Poland",
  "PT" => "Portugal",
  "RO" => "Romania",
  "SK" => "Slovakia (Slovak Republic)",
  "SI" => "Slovenia",
  "ES" => "Spain",
  "SE" => "Sweden",
  "GB" => "United Kingdom"
);

source

Leave a Comment :, more...

How to allow ad-sense bot to crawl phpbb3 private forums

by admin on May.29, 2009, under Communities

If you operate a forum with private sections and public section, you’ll find that the ads that ad-sense pushes on your website are created based solely on the public content. If you want more accurate result you may want to look into the ad-sense’ s “Site Authentication” feature. Here’s a small how-to on how to achieve this:

phpbb3

  • Create a user in the acp (for this example sake let’s use: adsense as username and adsense as password).
  • Give him permission based on your forum structure, we advise to give him read-only access on the whole forum.

ad-sense

  • go to the AdSense Setup tab, then click on the Site Authentication section.
  • click on the add a login link
  • Set it like this:
    • Restricted Directory or URL: the forum url (example: http://www.example.com/forum)
    • Authentication URL: the path to the ucp.php?mode=login file (example: http://www.example.com/forum/ucp.php?mode=login)
    • Authentication method: POST
    • Parameters: (you have to add them manually)
      • Attribute: login – Value: Login
      • Attribute: password – Value: userpassword (example: adsense)
      • Attribute: username – Value: username (example: adsense)
  • Now click on: Test My Authentication URL to check if everything went ok
    • Hope this will help you

Leave a Comment :, , more...

print_r output beautifier

by admin on May.13, 2009, under Programming

Today I wrote a script that needed to manipulate several multidimensional arrays, some of them pretty deep.

The standard php function print_r is an hellish mess, browsing through the marvellous php.net documentation I’ve found several examples to beautify the output of print_r (just to give credit: “Bob” and “Everett” were my inspiration) since no one actually did it the way I wanted I made a custom one myself:

function print_rr($array){
    foreach($array as $key=>$value){
       if(is_array($value)){
          $id = md5(rand());
          echo '[<a href="#" onclick="return expandParent(\''.$id.'\')">'.$key.'</a>]<br />';
          echo '<div id="'.$id.'" style="display:none;margin:10px;border-left:1px solid; padding-left:5px;">';
          print_rr($value, $count);
          echo '</div>';
       } else {
       echo "<b>$key</b>: ".$value."<br />";
       }
    }
echo '<script language="Javascript">
function expandParent(id){toggle="block";if(document.getElementById(id).style.display=="block"){toggle="none"}document.getElementById(id).style.display=toggle};
</script>';
}

Feel free to use it anyway you like it.

Leave a Comment :, , more...

Welcome

by admin on May.13, 2009, under 0plus1

Welcome to the official blog from 0plus1.

Leave a Comment :, more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Archives

All entries, chronologically...