Website Customer Visit Count

May 3rd, 2011 | by | technology

May
03

Question: A leading shopping website generates logs everyday of customers visiting their website along with the page number that they visited. Each entry contains the customer unique id and the page id, which the customer visited. Given the logs of 3 days, and the customer id, check whether the customer visited exactly on 2 out of 3 days and also check whether he visited more than 3 different pages?

#include <iostream>
#include <hash_map>
#include <list>
#include <set>
#include <bitset>
#include <string>

using namespace std;
using namespace stdext;

const int PAGE_VIEW_COUNT = 3;
const int DAYS_VISIT_COUNT = 2;

class CustomerData
{
public:
  bitset<PAGE_VIEW_COUNT> days;
  set<int> pages;

  CustomerData(bitset<PAGE_VIEW_COUNT> d, set<int> p) : days(d), pages(p)
  {
  }

  CustomerData() : days(string("000")) //Use days.reset, if PAGE_VIEW_COUNT != 3
  {
  }
};

hash_map<int, CustomerData> entries;
hash_map<int, CustomerData>::const_iterator recordIter;
typedef pair<int, CustomerData> CustomerRecordEntry;

void GenerateCustomerList(const list<pair<int, int>>& cuslist, int logNum);

int main()
{
  list<pair<int, int>> log1, log2, log3;
  log1.push_back(make_pair(1,1));
  log1.push_back(make_pair(2,2));
  log1.push_back(make_pair(4,1));
  log1.push_back(make_pair(1,3));

  log2.push_back(make_pair(2,1));
  log2.push_back(make_pair(2,4));
  log2.push_back(make_pair(4,2));
  log2.push_back(make_pair(1,3));
  log2.push_back(make_pair(1,5));

  log3.push_back(make_pair(3,2));
  log3.push_back(make_pair(4,4));
  log3.push_back(make_pair(5,2));
  log3.push_back(make_pair(5,5));

  GenerateCustomerList(log1, 1);
  GenerateCustomerList(log2, 2);
  GenerateCustomerList(log3, 3);

  int cid = 0;
  cout << "Enter customer ID: " ;
  cin >> cid;

  recordIter = entries.find(cid);
  if(recordIter != entries.end())
  {
    CustomerData data = (CustomerData)recordIter->second;
    if(data.days.count() == DAYS_VISIT_COUNT && data.pages.size() == PAGE_VIEW_COUNT)
    {
      cout << "Customer visited on exactly 2 days and have 3 or more distinct page views."
        << endl;
    }
    else
    {
      cout <<
        "Customer didn't visited on exactly 2 days or have 3 or more distinct page views."
        << endl;
    }
  }
  else
    cerr << "Customer ID not found in logs." << endl;

  cout << endl;
  return 0;
}

void GenerateCustomerList(const list<pair<int, int>>& cuslist, int logNum)
{
  list<pair<int, int>>::const_iterator iter;
  for(iter = cuslist.begin(); iter != cuslist.end(); iter++)
  {
    recordIter = entries.find(iter->first);
    if(recordIter != entries.end())
    {
      CustomerData data = (CustomerData)recordIter->second;
      data.days.set((logNum-1), true);
      //Space optimization
      //Check if already have 3 distinct page view, no need to add more
      if(data.pages.size() < PAGE_VIEW_COUNT)
        data.pages.insert(iter->second);
      entries[recordIter->first] = data;
    }
    else
    {
      bitset<PAGE_VIEW_COUNT> days(string("000"));
      days.set((logNum-1), true);
      set<int> pages;
      pages.insert(iter->second);
      CustomerData data(days, pages);
      entries.insert(CustomerRecordEntry(iter->first, data));
    }
  }
}

Comments Closed

The Intelligent Smoker

November 17th, 2009 | by | technology

Nov
17

A person smokes cigarettes and leaves the lower portion (around 1/5) of it unburnt. He then combines the 5 residues of those cigarettes to create a new one for smoking.

If the person had initially 93 cigarettes, how many cigarettes did he actually smoked?

He will smoke 116 cigarettes.

#include <utility>
#include <iostream>

typedef std::pair<int,double> Cigarettes;
using namespace std;

#define PIECES_REQD_TO_CREATE_A_NEW_CIGARETTE 5

Cigarettes cigarette(Cigarettes count);

int main()
{
  int initialCount = 0;
  cout << "Enter the number of initial cigarettes: ";
  cin >> initialCount;
  Cigarettes count = make_pair(initialCount, 0.0);
  cout << "The person smoked " << cigarette(count).first << " number of cigarettes." << endl;
  return 0;
}

Cigarettes cigarette(Cigarettes count)
{
  if(count.first > 0)
  {
    Cigarettes tmp = make_pair(count.first/PIECES_REQD_TO_CREATE_A_NEW_CIGARETTE,
      count.second + ((count.first%PIECES_REQD_TO_CREATE_A_NEW_CIGARETTE)/
      (PIECES_REQD_TO_CREATE_A_NEW_CIGARETTE+0.0)));
    if(tmp.second >= 1.0)
    {
      tmp.first += 1;
      tmp.second -= 1;
    }
    tmp = cigarette(tmp);
    count.first += tmp.first;
    count.second = tmp.second;
  }
  return count;
}

No Comments »

Programming Puzzle: Train Timetable

November 6th, 2009 | by | technology

Nov
06

A train line has two stations on it, A and B. Trains can take trips from A to B or from B to A multiple times during a day. When a train arrives at B from A (or arrives at A from B), it needs a certain amount of time before it is ready to take the return journey – this is the turnaround time. For example, if a train arrives at
12:00 and the turnaround time is 0 minutes, it can leave immediately, at 12:00.

A train timetable specifies departure and arrival time of all trips between A and B. The train company needs to know how many trains have to start the day at A and B in order to make the timetable work: whenever a train is supposed to leave A or B, there must actually be one there ready to go. There are passing sections on the track, so trains don’t necessarily arrive in the same order that they leave. Trains may not travel on trips that do not appear on the schedule.

Input:

The first line of input gives the number of cases, N. N test cases follow.

Each case contains a number of lines. The first line is the turnaround time, T, in minutes. The next line has two numbers on it, NA and NB. NA is the number of trips from A to B, and NB is the number of trips from B to A. Then there are NA lines giving the details of the trips from A to B.

Each line contains two fields, giving the HH:MM departure and arrival time for that trip. The departure time for each trip will be earlier than the arrival time. All arrivals and departures occur on the same day. The trips may appear in any order – they are not necessarily sorted by time. The hour and minute values are both two digits, zero-padded, and are on a 24-hour clock (00:00 through 23:59).

After these NA lines, there are NB lines giving the departure and arrival times for the trips from B to A.

Output:

For each test case, output one line containing “Case #x: ” followed by the number of trains that must start at A and the number of trains that must start at B.

Limits:

1 <= N <= 100

Small dataset:

0 <= NA, NB <= 20
0 <= T <= 5

Large dataset:

0 <= NA, NB <= 100
0 <= T <= 60

Input
———–
2
5
3 2
09:00 12:00
10:00 13:00
11:00 12:30
12:02 15:00
09:00 10:30
2
2 0
09:00 09:01
12:00 12:02

Output
————
Case #1: 2 2
Case #2: 2 0

// Train Timetable
// Author: Priyank Bolia <http://priyank.co.in>
// Date: 17th July, 2008

#include <iostream>
#include <fstream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <stdlib.h>

using namespace std;

typedef pair<int, int> TrainTime;
typedef vector<int> Time;
typedef vector<pair<int, int>> TimeTable;

std::vector<std::string> tokenize_str(const std::string &str,
      const std::string &delims);
bool value_comparer(const std::pair<int, int> &lhs,
      const std::pair<int, int> &rhs);
TrainTime ConvertToTimeTableEntry(string &s, int turnAround);
size_t TrainsNeeded(TimeTable &a, TimeTable &b);

int main()
{
  ifstream inputFile ("B-large.in");
  ofstream outputFile ("output.in");
  if(!inputFile.is_open())
    return 0;
  if(!outputFile.is_open())
    return 0;
  int totalCases = 0;
  inputFile >> totalCases;
  for (int i=0; i < totalCases; ++i)
  {
    int turnaroundTime = 0;
    int trainsFromA = 0;
    int trainsFromB = 0;
    TimeTable stationA;
    TimeTable stationB;
    string line;
    inputFile >> turnaroundTime;
    getline(inputFile, line);  //Get to next line
    inputFile >> trainsFromA;
    inputFile >> trainsFromB;
    getline(inputFile, line);  //Get to next line
    for (int j=0; j < trainsFromA; ++j)
    {
      getline(inputFile, line);
      stationA.push_back(ConvertToTimeTableEntry(line, turnaroundTime));
    }
    for (int j=0; j < trainsFromB; ++j)
    {
      getline(inputFile, line);
      stationB.push_back(ConvertToTimeTableEntry(line, turnaroundTime));
    }
    std::sort(stationA.begin(), stationA.end(), value_comparer);
    std::sort(stationB.begin(), stationB.end(), value_comparer);
    outputFile << "Case #" << (i+1) << ": " << TrainsNeeded(stationA, stationB) << " "
      << TrainsNeeded(stationB, stationA) << endl;
  }
  inputFile.close();
  outputFile.close();
  return 0;
}

size_t TrainsNeeded(TimeTable &a, TimeTable &b)
{
  size_t trainsNeeded = 0;
  Time trainsArrivingFromB;
  for(TimeTable::const_iterator iter = b.begin(); iter != b.end(); ++iter)
  {
    trainsArrivingFromB.push_back((*iter).second);
  }
  std::sort(trainsArrivingFromB.begin(), trainsArrivingFromB.end(), less<int>());
  size_t indexB = 0;
  if(trainsArrivingFromB.size() == 0)
  {
    trainsNeeded = (size_t)a.size();
  }
  else
  {
    for(TimeTable::const_iterator iter = a.begin(); iter != a.end(); ++iter)
    {
      if(indexB < trainsArrivingFromB.size() && (*iter).first < trainsArrivingFromB[indexB])
        ++trainsNeeded;
      else if(indexB == trainsArrivingFromB.size())
        ++trainsNeeded;
      else
        ++indexB;
    }
  }
  return trainsNeeded;
}

bool value_comparer(const std::pair<int, int> &lhs,
      const std::pair<int, int> &rhs)
{
  return lhs.first < rhs.first;
}

TrainTime ConvertToTimeTableEntry(string &s, int turnAround)
{
  vector<string> timing(tokenize_str(s, " "));
  vector<string> startTime(tokenize_str(timing[0], ":"));
  vector<string> finishTime(tokenize_str(timing[1], ":"));
  int startAtMin = (atoi(startTime[0].c_str())*60) + atoi(startTime[1].c_str());
  int finishAtMin = (atoi(finishTime[0].c_str())*60) + atoi(finishTime[1].c_str());
  return make_pair<int, int>(startAtMin, finishAtMin+turnAround);
}

std::vector<std::string> tokenize_str(const std::string &str,
      const std::string &delims)
{
  using namespace std;
  // Skip delims at beginning, find start of first token
  string::size_type lastPos = str.find_first_not_of(delims, 0);
  // Find next delimiter @ end of token
  string::size_type pos     = str.find_first_of(delims, lastPos);

  // output vector
  vector<string> tokens;

  while (string::npos != pos || string::npos != lastPos)
  {
    // Found a token, add it to the vector.
    tokens.push_back(str.substr(lastPos, pos - lastPos));
    // Skip delims.  Note the "not_of". this is beginning of token
    lastPos = str.find_first_not_of(delims, pos);
    // Find next delimiter at end of token.
    pos     = str.find_first_of(delims, lastPos);
  }

  return tokens;
}

No Comments »

Programming Puzzle: Search Engine

November 6th, 2009 | by | technology

Nov
06

The urban legend goes that if you go to the Google homepage and search for “Google”, the universe will implode. We have a secret to share… It is true! Please don’t try it, or tell anyone. All right, maybe not. We are just kidding. The same is not true for a universe far far away. In that universe, if you search on any search engine for that search engine’s name, the universe does implode! To combat this, people came up with an interesting solution. All queries are pooled together. They are passed to a central system that decides which query goes to which search engine. The central system sends a series of queries to one search engine, and can switch to another at any time. Queries must be processed in the order they’re received. The central system must never send a query to a search engine whose name matches the query. In order to reduce costs, the number of switches should be minimized. Your task is to tell us how many times the central system will have to switch between search engines, assuming that we program it optimally.

Input:

The first line of the input file contains the number of cases, N. N test cases follow. Each case starts with the number S — the number of search engines. The next S lines each contain the name of a search engine. Each search engine name is no more than one hundred characters long and contains only uppercase letters, lowercase letters, spaces, and numbers. There will not be two search engines with the same name. The following line contains a number Q — the number of incoming queries. The next Q lines will each contain a query. Each query will be the name of a search engine in the case.

Output:

For each input case, you should output:
Case #X: Y
where X is the number of the test case and Y is the number of search engine switches. Do not count the initial choice of a search engine as a switch.

Limits:

0 < N <= 20

Small dataset:

2 <= S <= 10
0 <= Q <= 100

Large dataset:

2 <= S <= 100
0 <= Q <= 1000

Input
———
2
5
Yeehaw
NSM
Dont Ask
B9
Googol
10
Yeehaw
Yeehaw
Googol
B9
Googol
NSM
B9
NSM
Dont Ask
Googol
5
Yeehaw
NSM
Dont Ask
B9
Googol
7
Googol
Dont Ask
NSM
NSM
Yeehaw
Yeehaw
Googol

Output
———–
Case #1: 1
Case #2: 0

// Saving the Universe
// Author: Priyank Bolia <http://priyank.co.in>
// Date: 17th July, 2008

#include <iostream>
#include <fstream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>

using namespace std;

bool value_comparer(const std::pair<std::string, size_t> &lhs,
        const std::pair<std::string, size_t> &rhs);
int SearchCount(vector<string>::iterator &enginesStart, vector<string>::iterator &enginesEnd,
      vector<string>::iterator &inputsStart, vector<string>::iterator &inputsEnd);

int main()
{
  ifstream inputFile ("A-large.in");
  ofstream outputFile ("output.in");
  if(!inputFile.is_open())
    return 0;
  if(!outputFile.is_open())
    return 0;
  int totalCases = 0;
  inputFile >> totalCases;
  for (int i=0; i < totalCases; ++i)
  {
    int searchEnginesCount = 0;
    int searchInputsCounts = 0;
    vector<string> searchEngines;
    vector<string> searchInputs;
    string line;
    inputFile >> searchEnginesCount;
    getline(inputFile, line);  //Get to next line
    for (int j=0; j < searchEnginesCount; ++j)
    {
      getline(inputFile, line);
      searchEngines.push_back(line);
    }
    inputFile >> searchInputsCounts;
    getline(inputFile, line);  //Get to next line
    for (int j=0; j < searchInputsCounts; ++j)
    {
      getline(inputFile, line);
      searchInputs.push_back(line);
    }
    int minSearch = SearchCount(searchEngines.begin(), searchEngines.end(),
      searchInputs.begin(), searchInputs.end());
    if(minSearch <= 0)
      minSearch = 1;
    outputFile << "Case #" << (i+1) << ": " << (minSearch-1) << endl;
  }
  inputFile.close();
  outputFile.close();
  return 0;
}

int SearchCount(vector<string>::iterator &enginesStart, vector<string>::iterator &enginesEnd,
      vector<string>::iterator &inputsStart, vector<string>::iterator &inputsEnd)
{
  map<string, size_t> maxSearch;
  if(distance(inputsStart, inputsEnd) <= 0)
    return 0;
  for(vector<string>::iterator iter = enginesStart; iter != enginesEnd; ++iter)
  {
    maxSearch[*iter] = distance(inputsStart, find(inputsStart, inputsEnd, *iter));
  }
  map<string, size_t>::const_iterator searchEngine =
    std::max_element(maxSearch.begin(), maxSearch.end(), value_comparer);
  return 1 + SearchCount(enginesStart, enginesEnd,
    (inputsStart+searchEngine->second), inputsEnd);
}

bool value_comparer(const std::pair<std::string, size_t> &lhs,
      const std::pair<std::string, size_t> &rhs)
{
  return lhs.second < rhs.second;
}

No Comments »

How to reverse the order of the words in a sentence?

November 3rd, 2009 | by | technology

Nov
03

For example for a given string: “This is just a test“, convert it to “test a just is This“.

std::string reverse_words(const std::string &strInput)
{
    using namespace std;
    string result;
    string::const_reverse_iterator iterWordStart, iterWordEnd;
    bool bBufferMode = true;
    iterWordStart = iterWordEnd = strInput.rbegin();
    for(; iterWordStart != strInput.rend(); ++iterWordStart) {
        if(!(isalpha(*iterWordStart) && bBufferMode)) {
            reverse_copy(iterWordEnd, iterWordStart, inserter(result, result.end()));
            iterWordEnd = iterWordStart;
            bBufferMode = !bBufferMode;
        }
    }
    reverse_copy(iterWordEnd, iterWordStart, inserter(result, result.end()));
    return result;
}

No Comments »