This challenge was posted by Groupon.
--
This isn't an algorithmic problem. Therefore, I think it's quite simple. My solution was in PHP:
Spoiler:PHP Code:
<?php
class GrouponFraud {
private $fraud;
public function __construct() {
// Index elements
define('ORDER_ID', 0);
define('DEAL_ID', 1);
define('EMAIL', 2);
define('STREET', 3);
define('CITY', 4);
define('STATE', 5);
define('ZIP', 6);
define('CC', 7);
define('ORDER', 8);
$this->fraud = array();
}
// Get the fraudulent cases
public function getFraud() {
ksort($this->fraud);
return $this->fraud;
}
// Whether or not an order is deemed fraudulent
public function isFraud($order, $subset) {
$count = 0;
foreach ( $subset as $sOrder ) {
if ( ( is_array( $order ) && is_array( $sOrder ) ) == false ) {
continue;
}
if ( $this->isFraud1($order, $sOrder) || $this->isFraud2($order, $sOrder) ) {
$this->fraud[intval($order[ORDER_ID])] = true;
$this->fraud[intval($sOrder[ORDER_ID])] = true;
return true;
}
}
return false;
}
// Let's check the first condition - same email, different credit card
private function isFraud1($order, $comparison) {
if ( $this->equalArray($order, $comparison) == true ) {
return false;
}
return $this->sameEmail( $order[EMAIL], $comparison[EMAIL] ) == true && ( $this->sameCC($order[CC], $comparison[CC]) == false );
}
// Let's check the second condition - same address/city/state/zip, different CC, regardless of emailo
private function isFraud2($order, $comparison) {
// If the two arrays are identical, we can just return false - they're the same order, no fraud here.
if ( $this->equalArray($order, $comparison) == true ) {
return false;
}
return ( $this->sameStreet($order[STREET], $comparison[STREET]) && $this->sameState( $order[STATE], $comparison[STATE] ) && $this->sameZip( $order[ZIP], $comparison[ZIP] ) && $this->sameCity($order[CITY], $comparison[CITY]) );
}
// Checks whether two credit cards are the same
private function sameCC($cc1, $cc2) {
return $this->cleanCC($cc1) == $this->cleanCC($cc2);
}
// Clean the credit card
private function cleanCC($cc) {
return preg_replace('/[^\d]/', '', $cc);
}
// Checks whether two orders have the same city
private function sameCity($city1, $city2) {
return $this->cleanCity($city1) == $this->cleanCity($city2);
}
// 'Clean' the city
private function cleanCity($city) {
return strtolower($city);
}
// Checks whether the zip code is the same for two order
private function sameZip($zip1, $zip2) {
return $this->cleanZip($zip1) == $this->cleanZip($zip2);
}
// 'Cleans' the zip code
private function cleanZip($zip) {
return preg_replace('/[^\d]/', '', $zip);
}
// Checks whether 2 orders have the same state
private function sameState($state1, $state2) {
return $this->cleanState($state1) == $this->cleanState($state2);
}
// 'Clean' the state
private function cleanState($state) {
$clean = strtolower($state);
$clean = $this->removeWhitespace($clean);
if ( $clean == 'il' ) {
$clean = 'illinois';
}
else if ( $clean == 'ca' ) {
$clean = 'california';
}
else if ( $clean == 'ny' ) {
$clean = 'new york';
}
return $clean;
}
// Remove excess whitespace
private function removeWhitespace($clean) {
$clean = trim($clean);
$clean = preg_replace('/\s\s+/', ' ', $clean);
if ( in_array( substr($clean, -1 ), array("\n", "\t", "\r", " " ) ) ) {
$clean = substr($clean, 0, strlen($clean) - 1 );
}
if ( in_array( substr($clean, 0, 1 ), array("\n", "\t", "\r", " " ) ) ) {
$clean = substr($clean, 1, strlen($clean) );
}
return $clean;
}
// Checks whether 2 orders have the same street
private function sameStreet($street1, $street2) {
return $this->cleanStreet($street1) == $this->cleanStreet($street2);
}
// 'Clean' the street
public function cleanStreet($street) {
$clean = strtolower($street);
$clean = $this->removeWhitespace($clean);
$clean .= PHP_EOL;
$clean = str_replace( array(' street' . PHP_EOL, ' road' . PHP_EOL), array(' st.', ' rd.'), $clean );
$clean = str_replace(PHP_EOL, '', $clean);
return $clean;
}
// Checks whether two arrays are identical
private function equalArray($arr1, $arr2) {
return ! array_diff( $arr1, $arr2 ) && ! array_diff( $arr2, $arr1 );
}
//private Checks whether or not they have the same email address
private function sameEmail($first, $second) {
return $this->cleanEmail($first) == $this->cleanEmail($second);
}
// 'Clean' an email (remove all attempts to trick it)
private function cleanEmail($email) {
$clean = strtolower($email);
$clean = $this->removeWhitespace($clean);
$username = current(explode("@", $clean));
$rest = str_replace($username, '', $clean); // Rest of the email portion
$username = str_replace('.', '', $username); // Remove full stops
$username = current(explode("+", $username, 2)); // Remove all plus signs
return $username . $rest;
}
}
$input = '5
1,1,bugs@BUNNY.com,123 Sesame St.,New York,NY,10011,12345689010
3,4,bugs+bunny@bunny.com,123 Sesame St.,New York,NY,10011,12345689010
2,2,elmer@fudd.com,123 Sesame St.,New York,NY,10011,10987654321
5,6,bugs@bunny.com,123 Sesame street,New York,NY,10011,123456839010
4,3,bugs@bunny.com,123 Sesame street,New York,NY,10011,123456839010';
// Sort the orders in to deals by deal id
$grouponFraud = new GrouponFraud;
$orders = array();
foreach ( explode( "\n", $input ) as $id => $order ) {
if ( $id == 0 ) {
$limit = (int) $order;
continue;
}
if ( $id > $limit ) {
break;
}
list($orderId, $dealId,$rest) = explode(",", $order, 3);
$arr = explode(",", $order, 8);
$arr[] = $id;
$orders[intval($dealId)][] = $arr;
}
// Let's detect fraudulent errors
foreach ( $orders as $dealId => $subset ) {
foreach ( $subset as $order ) {
$grouponFraud->isFraud( $order, $subset );
}
}
// Let's output the fraudulent cases
$str = "";
foreach ( $grouponFraud->getFraud() as $id => $val ) {
$str .= $id . ",";
}
if ( strlen( $str ) > 0 ) {
$str = substr( $str, 0, strlen($str) - 1 );
}
echo $str;
Results 1 to 1 of 1
- 15 Jan. 2012 03:47am #1
Interviewstreet CS 2 - Fraud Prevention