Wednesday 30 December 2015

Find the Nth Multiple Given a Set

1. Problem Description
This is a Google interview question for software development engineer from careercup. Here is the original thread,
"
Find the n-th smallest multiple given a set of numbers. For example, set = {4, 6}, n = 6: 

The sequence is:

4, 6, 8, 12, 16, 18, etc...

Answer is 18
- supatroopa 19 days ago in United States | Report Duplicate | Flag ".

2. Analysis
A similar sort of question can be found here, Find the Nth Expressible Number of Given Set of Prime Numbers.This interview question is relatively simpler than finding the Nth expressible number of a given set of prime number.
For each number in the given set, the following sequence number is quite simple to generate - increment by itself on its previous number (multiple). And the only pitfall is that don't count the common divider by multiple times. This pitfall can be easily avoided as the min-priority-heap will squeeze out the minimal value of multiples of all elements in the given set. Compare the value at the top of min-priority-heap with last value and skip it (don't count this time) if they're equal. Because it is one of common dividers of at least of two elements in the given set. Keep doing this until reach Nth.

3. C++ Implementation
// **********************************************************************************
// Implementation
// **********************************************************************************
#include <queue>
#include <vector>

struct ValueBasePair
{
    ValueBasePair(unsigned long val, unsigned long b)
        : value(val), base(b)
    {}
    unsigned long value;
    unsigned long base;
};

struct ValueBasePairComparator
{
    bool operator()(const ValueBasePair& lhs, const ValueBasePair& rhs) {
        return lhs.value > rhs.value;
    }
};

using MinHeap2 = std::priority_queue<ValueBasePair,
                                     std::vector<ValueBasePair>,
                                     ValueBasePairComparator>;

unsigned long FindNthMultipleNumGivenSet(const unsigned long Nth,
                                         MinHeap2& topNums)
{
    unsigned long numCounter = Nth;
    unsigned long lastVal = 0;
    while (true) {
        const ValueBasePair kvp = topNums.top();
        // pop out a value from the priority queue
        // count until the Nth
        // But only count if this number is different from the last
        // because common mulitple can only count as once
        // For example {4, 6}, numbers like 12, 24 count only once
        if (kvp.value != lastVal) {
            --numCounter;
            lastVal = kvp.value;
        }
        if (numCounter == 0) {
            return kvp.value;
        }

        // pop out the value from the priority queue
        // and push its successor from the value list into the priority queue
        // Successor: <multiple, base> -> <multiple + base, base>
        topNums.pop();
        topNums.push(ValueBasePair(kvp.value + kvp.base, kvp.base));
    }

    return 0;
}

unsigned long FindNthMultipleNumGivenSet(
    const std::vector<unsigned long>& sortedPrimes,
    const unsigned long Nth)
{
    if (sortedPrimes.empty() || Nth == 0) {
        // indicates failure
        return 0;
    }

    if (Nth <= sortedPrimes.size()) {
        return sortedPrimes[Nth - 1];
    }

    MinHeap2 topNums;
    auto iterEnd = sortedPrimes.end();
    // Starting point:
    // the values: each number x 1
    // the priority queue is (multiple, base) - <value, base> pair
    for (auto iter = sortedPrimes.begin(); iter != iterEnd; ++iter) {
        topNums.push(ValueBasePair(*iter, *iter));
    }

    return FindNthMultipleNumGivenSet(sortedPrimes, Nth, topNums);
}
// **********************************************************************************
// Test
// **********************************************************************************
#include <cassert>

void TestFindNthMultipleNum()
{
    std::vector<unsigned long> sortedPrimes;
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 0) == 0);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 1) == 0);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 5) == 0);

    sortedPrimes.push_back(4);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 0) == 0);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 1) == 4);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 5) == 20);

    sortedPrimes.push_back(6);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 0) == 0);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 1) == 4);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 2) == 6);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 3) == 8);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 4) == 12);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 5) == 16);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 6) == 18);
}

Tuesday 29 December 2015

Dynamic Programming - Generate Permutation Set

1. Problem Description

- PradeepN about a month ago in United States | Report Duplicate | Flag ".

2. Analysis
This is a similar sort of problem like Dynamic programming - Generate power set given a set. Again the key is to find the sub-problem.
Assume a given set as S with M elements and N, S = {x1, x2, ..., xm}.
    - N = 1, F(1) = {{x1}, {x2}, ..., {xm}}
    - N = 2, F(2) = {{x1, x1}, {x1, x2}, ..., {x1, xm},
                            {x2, x1}, {x2, x2}, ..., {x2, xm},
                                       ......
                            {xm, x1}, {xm, x1}, ..., {xm, xm}}
    - Assume N-1, F(N-1) = {X1, X2, ..., Xk}, where k = M^(N-1)
    - N, F(N) = {{X1, x1}, {X1, x2}, ..., {X1, xm},
                       {X2, x1}, {X2, x2}, ..., {X2, xm},
                                      ......
                       {Xk, xm},  {Xk, xm}, ..., {Xk, xm}}, total k*M = M^N

As long as F(n-1) is known, then F(n) can be compute as each set of F(n-1) combining with each element in S to form the new set. And the size of set can be easily computed as M^n.

3. C++ Implementation and Test
- Given set with size M
- Given N.
    * if N = 0, then return empty set
    * if N > M, then return empty set as well.
    * Otherwise return the permutation set with size of power(M, N).

// ********************************************************************************
// Implementation
// ********************************************************************************
#include <vector>

template<typename T>
void PermuteSet(std::vector<T> const &input, size_t len, std::vector<std::vector<T>> &result)
{
    if (len == 0) {
        return;
    }

    const size_t currentSize = result.size();
    auto iter = input.begin();
    auto iterEnd = input.end();
    if (currentSize == 0) {
        // very first time
        for (; iter != iterEnd; ++iter) {
            std::vector<T> initialSet(1, *iter);
            result.push_back(initialSet);
        }
    }
    else {
        auto iterBeginOfResult = result.begin();
        ++iter;
        // Keep F(n-1) and generate the values for F(n)
        for (; iter != iterEnd; ++iter) {
            size_t count = 0;
            for (auto iterOfResult = iterBeginOfResult; count < currentSize; ++iterOfResult, ++count) {
                std::vector<T> copy(*iterOfResult);
                copy.push_back(*iter);
                result.push_back(copy);
            }
        }

        // Update F(n-1) as elements of F(n) 
        size_t count = 0;
        auto firstVal = input.begin();
        for (auto iterOfResult = iterBeginOfResult; count < currentSize; ++iterOfResult, ++count) {
            (*iterOfResult).push_back(*firstVal);
        }
    }

    PermuteSet(input, len - 1, result);
}

template<typename T>
std::vector<std::vector<T>> PermuteSet(std::vector<T> const &input, size_t len)
{
    if (input.empty() || len == 0 || len > input.size()) {
        return std::vector<std::vector<T>>();
    }

    std::vector<std::vector<T>> result;
    result.reserve(static_cast<size_t>(pow(input.size(), len)));
    PermuteSet(input, len, result);

    return result;
}

// ********************************************************************************
// Test
// ********************************************************************************
#include <cassert>
void TestSetPermutation()
{
    {
        const std::vector<int> input;
        auto result = PermuteSet(input, 0);
        assert(result.empty() == true);
        result = PermuteSet(input, 1);
        assert(result.empty() == true);
    }

    {
        const std::vector<int> input = { 1 };
        auto result = PermuteSet(input, 0);
        assert(result.empty() == true);
        result = PermuteSet(input, 2);
        assert(result.empty() == true);
        result = PermuteSet(input, 1);
        assert(result.size() == 1);
        assert(result[0].at(0) == 1);
    }

    {
        const std::vector<int> input = { 2, 3, 4, 5 };
        auto result = PermuteSet(input, 0);
        assert(result.empty() == true);

        result = PermuteSet(input, 1);
        assert(result.size() == 4);
        assert(result[0].size() == 1);
        assert(result[0].at(0) == 2);
        assert(result[1].size() == 1);
        assert(result[1].at(0) == 3);
        assert(result[2].size() == 1);
        assert(result[2].at(0) == 4);
        assert(result[3].size() == 1);
        assert(result[3].at(0) == 5);

        result = PermuteSet(input, 2);
        assert(result.size() == 16);
        for (auto iter = result.begin(); iter != result.end(); ++iter) {
            assert(iter->size() == 2);
        }
        assert((*result.begin()) == std::vector<int>({2, 2}));
        assert((*result.rbegin()) == std::vector<int>({5, 5}));

        result = PermuteSet(input, 3);
        assert(result.size() == 64);
        for (auto iter = result.begin(); iter != result.end(); ++iter) {
            assert(iter->size() == 3);
        }
        assert((*result.begin()) == std::vector<int>({ 2, 2, 2 }));
        assert((*result.rbegin()) == std::vector<int>({ 5, 5, 5 }));

        result = PermuteSet(input, 4);
        assert(result.size() == 256);
        for (auto iter = result.begin(); iter != result.end(); ++iter) {
            assert(iter->size() == 4);
        }
        assert((*result.begin()) == std::vector<int>({ 2, 2, 2, 2 }));
        assert((*result.rbegin()) == std::vector<int>({ 5, 5, 5, 5 }));

        result = PermuteSet(input, 6);
        assert(result.empty() == true);
    }
}


Find the Shortest Sub-string Containing S1, S2 and S3

1. Problem Description
- witold.pluta42 about a month ago in Europe | Report Duplicate | Flag ". 

2. Solution
Given the sting as S and 3 smaller string S1, S2 and S3, the solution has O(1) memory complexity and O(N) computation complexity, where N is the size of S. The idea is to go through the string, find the position of S1, S2 and S3, always keep the latest position of 3 and track the best result.

Pseudo-code:
    1. Initialization:
        * The starting position of idxS1, idxS2, idxS3 in S as -1
        * The length of 3 sub-string as len1, len2 and len3
        * minLen: the minimal value of len1, len2 and len3
        * maxLen: the maximal value of len1, len2 and len3
        * The length of S as len
        * The result sub-string, result, as S
    2. Start from the beginning of string S, idx = 0
    3. Check if S1 exists from idx
        Go to Step 4, if yes.
        Go to Step 5, if no
    4. Update idxS1 = idx, and calculate the sub-string if idxS1, idxS2 and idxS3 all >= 0
        * The start position of sub-string: the minimal value of idxS1, idxS2 and idxS3
        * The end position: the maximal value of idxS1+len1, idxS2+len2 and idxS3+len3
        * Swap it with result it is shorter
    5. Check if S2 exists from idx
        Go to Step 6, if yes.
        Go to Step 7, if no
    6. Update idxS2 = idx, and calculate the sub-string if idxS1, idxS2 and idxS3 all >= 0
        The start position of substring: the minimal value of idxS1, idxS2 and idxS3
        The end position: the maximal value of idxS1+len1, idxS2+len2 and idxS3+len3
        Swap it with result it is shorter
    7. Check if S3 exists from idx
        Go to Step 8, if yes.
        Go to Step 9, if no
    9. Update idxS3 = idx, and calculate the sub-string if idxS1, idxS2 and idxS3 all >= 0
        The start position of substring: the minimal value of idxS1, idxS2 and idxS3
        The end position: the maximal value of idxS1+len1, idxS2+len2 and idxS3+len3
        Swap it with result it is shorter
    10, Go to Step 12, if result's length is equal to maxLen
    11. Increment idx by 1 until it reaches (len - minLen) and repeat Step 3~10
    12. Check if idxS1, idxS2 and idxS3 are all >=0
         Return the sub-string of result from S, if yes
         Return empty string otherwise

Note: 
Step 10: the condition only happens when the longest string of S1, S2 and S3 contains other 2. Then the shortest sub-string will be the longest string of S1, S2 and S3.
Step 11: No need to loop until the end of S, because when idx is >= (len-minLen), then Step 3, 5 and 7 will be always false.

3. C++ Implementation
// ********************************************************************************
// Implementation
// ********************************************************************************
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>

struct SubString {
    SubString(size_t idx, size_t length)
        : startIndex(idx)
        , len(length)
    {}

    size_t startIndex;
    size_t len;

    size_t EndIndex() const {
        return startIndex + len;
    }

    bool operator > (SubString const & rhs) {
        return this->len > rhs.len;
    }
};

SubString FindSubStringContainS1S2S3(SubString const &s1,
                                     SubString const &s2,
                                     SubString const &s3)
{
    size_t startIndex = std::min(std::min(s1.startIndex, s2.startIndex), s3.startIndex);
    size_t endIndex = std::max(std::max(s1.EndIndex(), s2.EndIndex()), s3.EndIndex());
    return SubString(startIndex, endIndex - startIndex);
}

void FindShortestSubString(int idxS1, int idxS2, int idxS3,
                          size_t len1, size_t len2, size_t len3,
                          SubString& bestResult)
{
    if (idxS1 < 0 || idxS2 < 0 || idxS3 < 0) {
        return;
    }

    SubString temp = FindSubStringContainS1S2S3(SubString(idxS1, len1),
                                                SubString(idxS2, len2),
                                                SubString(idxS3, len3));
    if (bestResult.operator>(temp)) {
        bestResult = temp;
    }
}

std::string FindTheShortestSubStringContainS123(std::string const &input,
                                                std::string const &S1,
                                                std::string const &S2,
                                                std::string const &S3)
{
    if (input.empty() || S1.empty() || S2.empty() || S3.empty()) {
        return std::string();
    }

    const size_t len1 = S1.size();
    const size_t len2 = S2.size();
    const size_t len3 = S3.size();
    const size_t minLen = std::min(std::min(len1, len2), len3);
    const size_t maxLen = std::max(std::max(len1, len2), len3);
    const size_t lenInput = input.size();
    const char* cstrInput = input.c_str();
    const char* cstrS1 = S1.c_str();
    const char* cstrS2 = S2.c_str();
    const char* cstrS3 = S3.c_str();

    int idxS1 = -1;
    int idxS2 = -1;
    int idxS3 = -1;
    SubString bestResult(0, lenInput);
    for (size_t index = 0; (index + minLen) < lenInput; ++index) {
       if (strncmp(cstrInput + index, cstrS1, len1) == 0) {
            idxS1 = index;
            FindShortestSubString(idxS1, idxS2, idxS3, len1, len2, len3, bestResult);
        }
        if (strncmp(cstrInput + index, cstrS2, len2) == 0) {
            idxS2 = index;
            FindShortestSubString(idxS1, idxS2, idxS3, len1, len2, len3, bestResult);
        }
        if (strncmp(cstrInput + index, cstrS3, len3) == 0) {
            idxS3 = index;
            FindShortestSubString(idxS1, idxS2, idxS3, len1, len2, len3, bestResult);
        }

        if (bestResult.len == maxLen) {
            break;
        }
    }

    if (idxS1 < 0 || idxS2 < 0 || idxS3 < 0) {
        return std::string();
    }

    return input.substr(bestResult.startIndex, bestResult.len);
}

// ********************************************************************************
// Test
// ********************************************************************************
#include <cassert>
void TestFindTheShortestSubStringContainS1S2S3()
{
    {
        const std::string input("abc0123456789aksdfjasd");
        const std::string s1("0123");
        const std::string s2("3456");
        const std::string s3("");
        std::string result = FindTheShortestSubStringContainS123(input, s1, s2, s3);
        assert(result.empty() == true);
    }

    {
        const std::string input("abc0123456789aksdfjasd");
        const std::string s1("0123456");
        const std::string s2("3456");
        const std::string s3("1234");
        std::string result = FindTheShortestSubStringContainS123(input, s1, s2, s3);
        assert(result == std::string("0123456"));
    }

    {
        const std::string input("abc0123456789aksdfjasd");
        const std::string s1("0123");
        const std::string s2("3456");
        const std::string s3("6789");
        std::string result = FindTheShortestSubStringContainS123(input, s1, s2, s3);
        assert(result == std::string("0123456789"));
    }

    {
        const std::string input("sdfa01234ad23456dfad6789abc0123456789aksdfjasd");
        const std::string s1("0123");
        const std::string s2("3456");
        const std::string s3("6789");
        std::string result = FindTheShortestSubStringContainS123(input, s1, s2, s3);
        assert(result == std::string("0123456789"));
    }

    {
        const std::string input(
               "sdfa01234ad23456dfad6789abc0123456789aksdfjasd0123skd3456kjsd6789jhs");
        const std::string s1("0123");
        const std::string s2("3456");
        const std::string s3("6789");
        std::string result = FindTheShortestSubStringContainS123(input, s1, s2, s3);
        assert(result == std::string("0123456789"));
    }
}

Wednesday 9 December 2015

LinkedList - Find the Longest Palindrome

1. Problem Description
This is a a Microsoft interview question for software developer engineer from careercup. Here is the original thread,
"Given a singly connected linked list, find the largest palindrome in the list in O(1) space.
neer.1304 9 days ago in United States Report Duplicate | Flag ".

2. Algorithm
This is a more difficult question than Data Structure and Algorithm - Linked List: Palindrome and Data Structure and Algorithm - Linked List: Palindrome II, which require to use O(1) space to find out if a linked list is a palindrome. In order to achieve O(1) space the technique of splitting the linked list is still applicable for this question as well.

Pseudo-code:
    - Initialize startPtr as nullptr - points to the starting node of palindrome
    - Initialize len as the length of palindrome of 0
    - Return empty startPtr and zero len, if linked list is empty
    - Point startPtr to head and initialize len as 1
    - Split the linked list as two equal parts and count the size
        * If linked list has even number of nodes as S, then
           > middleNode point to the (S/2)th node
           > firstHalfCount as S/2 and totalCount as S
        * If odd number of node(s), then
           > middleNode points to (S/2+1)th node
           > firstHalfCount as S/2+1 and totalCount as S
    - Reverse the 1st half linked list and keep the 2nd half as firstHalfHead, secondHalfHead
    - If potential maximal palindrome size is greater than len
      (2*firstHalfCount + 1) > len, and firstHalfHead and secondHalfHead are valid, then
        - Compare firstHalfHead and secondHalfHead until the first difference appearing
        - The length is greater than len
           > Point startPtr to the last node of firstHalfHead that have the same value in both
           > Update len as the current length
        - Compare firstHalfHead and secondHalfHead->next until the first difference appears
        - If the length+1 is greater than len
           > Point startPtr to the last node of firstHalfHead that have the same value in both
           > Update len as the current length+1
        - Update:
           > firstHalfCount = firstHalfCount - 1;
           > Push front the head of firstHalfHead to secondHalfHead
           > Point firstHalfHead into its next
        - Keep loop this step until any of the condition does not hold
    - Recover the linked list
    - Split the linked list into two again and count the size
    - Reverse the 1st half and keep the 2nd half as firstHalfHead and secondHalfHead
    - Assign secondHalfCount = totalCount - firstHalfCount
    - If potential maximal palindrome size is greater than len,
      (2*secondHalfCount + 1) > len, and firstHalfHead and secondHalfHead are valid, then
        - Compare firstHalfHead and secondHalfHead until the first difference appearing
        - If the length is greater than len
           > Point startPtr to the last node of firstHalfHead that have the same value in both
           > Update len as the current length
        - Compare firstHalfHead->next with secondHalfHead until the first difference appears
        - If the length+1 is greater than len
           > Point startPtr to the last node of firstHalfHead that have the same value in both
           > Update len as the current length+1
        - Update:
           > secondHalfCount = secondHalfCount - 1
           > Push front the head of secondHalfHead to firstHalfHead
           > Point secondHalfHead into its next
        - Keep loop this step until any of the condition does not hold
    - Recover the linked list
    - Return startPtr and len

3. C++ Solution
// ********************************************************************************
// Implementation
// ********************************************************************************
#pragma once

#include "LinkedListAlgo.h"
#include "LinkedListElement.h"

#include <vector>

#include <cassert>

template<typename T>
class LinkedListLongestPalindrome
{
public:
    LinkedListLongestPalindrome() = default;
    LinkedListLongestPalindrome(LinkedListLongestPalindrome&) = delete;
    LinkedListLongestPalindrome(LinkedListLongestPalindrome&&) = delete;
    LinkedListLongestPalindrome& operator=(LinkedListLongestPalindrome&) = delete;
    LinkedListLongestPalindrome& operator=(LinkedListLongestPalindrome&&) = delete;
    ~LinkedListLongestPalindrome() = default;

    struct LL_PalindromeResult
    {
        LL_PalindromeResult()
            : LL_PalindromeResult(nullptr, 0)
        {}

        LL_PalindromeResult(LinkedListElement<T> *ptr, size_t l)
            : startPtr(ptr), len(l)
        {
        }
        LL_PalindromeResult(LL_PalindromeResult&) = default;
        ~LL_PalindromeResult() = default;
        LinkedListElement<T> *startPtr;
        size_t len;
    };

    LL_PalindromeResult operator()(LinkedListElement<T>* head)
    {
        LL_PalindromeResult result;
        if (head == nullptr) {
            return result;
        }

        result = LL_PalindromeResult(head, 1);
        if (head->next == nullptr) {
            return result;
        }

        // find the middle node
        LinkedListElement<T> *middleNode;
        size_t firstHalfCount;
        size_t totalCount;
        SplitLinkedListIntoTwo(head, middleNode, firstHalfCount, totalCount);
     
        // split the node
        LinkedListElement<T> *firstHalf;
        LinkedListElement<T> *secondHalf;
        SplitAndReverseTheFirstHalf(head, middleNode, firstHalf, secondHalf);

        // find the longest palindrome in the 1st run
        LL_PalindromeResult tempResult = FindLongestPalindrome(firstHalf,
                                                               secondHalf,
                                                               result.len,
                                                               firstHalfCount,
                                                               false);
        if (tempResult.len > result.len) {
            result = tempResult;
        }

        // recover the linked list
        RecoverFromSplitting(firstHalf, secondHalf);
        assert(firstHalf == head);

        // split the node again
        SplitAndReverseTheFirstHalf(head, middleNode, firstHalf, secondHalf);

        // find the longest palindrome in the 2nd run
        tempResult = FindLongestPalindrome(firstHalf,
                                           secondHalf,
                                           result.len,
                                           totalCount - firstHalfCount,
                                           true);
        if (tempResult.len > result.len) {
            result = tempResult;
        }

        // recover the linked list
        RecoverFromSplitting(firstHalf, secondHalf);

        return result;
    }

private:
    LL_PalindromeResult FindLongestPalindrome(LinkedListElement<T>* &firstHalf,
                                              LinkedListElement<T>* &secondHalf,
                                              size_t curMax,
                                              size_t halfCount,
                                              bool reversed)
    {
        LinkedListElement<T> *temp;
        LL_PalindromeResult result(nullptr, curMax);
        LL_PalindromeResult tempResult;
        while ((halfCount * 2 + 1) > result.len && firstHalf != nullptr && secondHalf != nullptr) {
            tempResult = GetCommon(firstHalf, secondHalf, reversed);
            // track the longest palindrome
            if (tempResult.len > result.len) {
                result = tempResult;
            }

            // update the node and count for the next loop
            if (reversed) {
                temp = secondHalf->next;
                secondHalf->next = firstHalf;
                firstHalf = secondHalf;
                secondHalf = temp;
            }
            else {
                temp = firstHalf->next;
                firstHalf->next = secondHalf;
                secondHalf = firstHalf;
                firstHalf = temp;
            }
            --halfCount;
        }
        return result;
    }

    LL_PalindromeResult GetCommon(LinkedListElement<T> *x,
                                                               LinkedListElement<T> *y,
                                                               bool reversed)
    {
        if (x == nullptr || y == nullptr) {
            return LL_PalindromeResult();
        }

        // compare firstHalf with secondHalf
        LL_PalindromeResult result1 = CompareCommon(x, y, 0);
        // depends on which run
        //    - compare firstHalf->next with secondHalf
        //    - compare firstHalf with secondHalf->next
        LL_PalindromeResult result2 = reversed ?
            CompareCommon(x->next, y, 1) : CompareCommon(x, y->next, 1);

        // return the longer palindrome
        return result1.len > result2.len ? result1 : result2;
    }

    LL_PalindromeResult CompareCommon(LinkedListElement<T> *x,
                                                                       LinkedListElement<T> *y,
                                                                       size_t curLongestLen)
    {
        // compare two nodes until the first diverge appears
        LL_PalindromeResult result(nullptr, curLongestLen);
        while (x != nullptr && y != nullptr) {
            if (x->data == y->data) {
                result.len += 2;
                result.startPtr = x;
                x = x->next;
                y = y->next;
            }
            else {
                break;
            }
        }

        return result;
    }

    void SplitLinkedListIntoTwo(LinkedListElement<T>* head,
                                LinkedListElement<T>* &middleNode,
                                size_t &firstHalfCount,
                                size_t &totalCount)
    {
        if (head == nullptr) {
            middleNode = nullptr;
            firstHalfCount = totalCount = 0;
            return;
        }
        // a, b, c, d, e, f, g, h
        // a, a
        // b, c
        // c, e
        // d, g
        // d, nullptr

        // a, b, c, d, e, f, g, h, i
        // a, a
        // b, c
        // c, e
        // d, g
        // e, i
        // e, nullptr
        LinkedListElement<T> *slowPtr = head;
        {
            LinkedListElement<T> *fasterX2 = head;
            while (fasterX2 != nullptr) {
                if (fasterX2->next != nullptr) {
                    ++totalCount;
                    fasterX2 = fasterX2->next;
                    if (fasterX2->next != nullptr) {
                        ++totalCount;
                        ++firstHalfCount;
                        slowPtr = slowPtr->next;
                    }
                }
                fasterX2 = fasterX2->next;
            }
        }
        middleNode = slowPtr;
    }

    void SplitAndReverseTheFirstHalf(LinkedListElement<T> *head,
                                   LinkedListElement<T> *middleNode,
                                   LinkedListElement<T>* &firstHalf,
                                   LinkedListElement<T>* &secondHalf)
    {
        if (middleNode == nullptr) {
            firstHalf = secondHalf = nullptr;
            return;
        }

        secondHalf = middleNode->next;
        middleNode->next = nullptr;
        firstHalf = head;
        LL_Reverse(&firstHalf);
    }

    void RecoverFromSplitting(LinkedListElement<T>* &firstHalf,
                              LinkedListElement<T>* &secondHalf)
    {
        LinkedListElement<T>* temp;
        if (firstHalf != nullptr) {
            temp = firstHalf;
            LL_Reverse(&firstHalf);
            temp->next = secondHalf;
        }
        else {
            firstHalf = secondHalf;
        }
    }
};

// ********************************************************************************
// Test
// ********************************************************************************
void TestLinkedListLongestPalindrome()
{
    using Result = LinkedListLongestPalindrome<char>::LL_PalindromeResult;
    {
        LinkedListElement<char> *inputLL = nullptr;
        Result r = LinkedListLongestPalindrome<char>().operator()(inputLL);
        assert(r.len == 0);
        assert(r.startPtr == nullptr);
    }
    {
        LinkedListElement<char> *inputLL = nullptr;
        LL_PushFront(&inputLL, 'A');
        Result r = LinkedListLongestPalindrome<char>().operator()(inputLL);
        assert(r.len == 1);
        assert(r.startPtr->data == 'A');
    }
    {
        LinkedListElement<char> *inputLL = nullptr;
        LL_PushFront(&inputLL, 'B');
        LL_PushFront(&inputLL, 'A');
        Result r = LinkedListLongestPalindrome<char>().operator()(inputLL);
        assert(r.len == 1);
        assert(r.startPtr->data == 'A');
    }
    {
        const std::string input("ABCDEFGHIJKLMN");
        LinkedListElement<char> *inputLL = nullptr;
        auto iterREnd = input.rend();
        for (auto iterR = input.rbegin(); iterR != iterREnd; ++iterR) {
            LL_PushFront(&inputLL, *iterR);
        }
        Result r = LinkedListLongestPalindrome<char>().operator()(inputLL);
        assert(r.len == 1);
        assert(r.startPtr->data == 'A');
    }
    {
        const std::string input("AAAAAAAAAAAAAAAAAAAAA");
        LinkedListElement<char> *inputLL = nullptr;
        auto iterREnd = input.rend();
        for (auto iterR = input.rbegin(); iterR != iterREnd; ++iterR) {
            LL_PushFront(&inputLL, *iterR);
        }
        Result r = LinkedListLongestPalindrome<char>().operator()(inputLL);

        std::string palindrome;
        while (r.len) {
            palindrome.push_back(r.startPtr->data);
            r.startPtr = r.startPtr->next;
            --r.len;
        }
        assert(palindrome == input);
    }
    {
        const std::string input("ABABCDEFGFEDCBAXY");
        LinkedListElement<char> *inputLL = nullptr;
        auto iterREnd = input.rend();
        for (auto iterR = input.rbegin(); iterR != iterREnd; ++iterR) {
            LL_PushFront(&inputLL, *iterR);
        }
        Result r = LinkedListLongestPalindrome<char>().operator()(inputLL);

        std::string palindrome;
        while (r.len) {
            palindrome.push_back(r.startPtr->data);
            r.startPtr = r.startPtr->next;
            --r.len;
        }
        assert(palindrome == "ABCDEFGFEDCBA");
    }
    {
        const std::string input("ABABCDEFGFEDCBAXYZUVWXYZ");
        LinkedListElement<char> *inputLL = nullptr;
        auto iterREnd = input.rend();
        for (auto iterR = input.rbegin(); iterR != iterREnd; ++iterR) {
            LL_PushFront(&inputLL, *iterR);
        }
        Result r = LinkedListLongestPalindrome<char>().operator()(inputLL);

        std::string palindrome;
        while (r.len) {
            palindrome.push_back(r.startPtr->data);
            r.startPtr = r.startPtr->next;
            --r.len;
        }
        assert(palindrome == "ABCDEFGFEDCBA");
    }
    {
        const std::string input("UVWXYZABABCDEFGFEDCBAXYZ");
        LinkedListElement<char> *inputLL = nullptr;
        auto iterREnd = input.rend();
        for (auto iterR = input.rbegin(); iterR != iterREnd; ++iterR) {
            LL_PushFront(&inputLL, *iterR);
        }
        Result r = LinkedListLongestPalindrome<char>().operator()(inputLL);

        std::string palindrome;
        while (r.len) {
            palindrome.push_back(r.startPtr->data);
            r.startPtr = r.startPtr->next;
            --r.len;
        }
        assert(palindrome == "ABCDEFGFEDCBA");
    }
    {
        const std::string input("AB01234567899876543210XY");
        LinkedListElement<char> *inputLL = nullptr;
        auto iterREnd = input.rend();
        for (auto iterR = input.rbegin(); iterR != iterREnd; ++iterR) {
            LL_PushFront(&inputLL, *iterR);
        }
        Result r = LinkedListLongestPalindrome<char>().operator()(inputLL);

        std::string palindrome;
        while (r.len) {
            palindrome.push_back(r.startPtr->data);
            r.startPtr = r.startPtr->next;
            --r.len;
        }
        assert(palindrome == "01234567899876543210");
    }
    {
        const std::string input("asfdasdfasAB01234567899876543210XY");
        LinkedListElement<char> *inputLL = nullptr;
        auto iterREnd = input.rend();
        for (auto iterR = input.rbegin(); iterR != iterREnd; ++iterR) {
            LL_PushFront(&inputLL, *iterR);
        }
        Result r = LinkedListLongestPalindrome<char>().operator()(inputLL);

        std::string palindrome;
        while (r.len) {
            palindrome.push_back(r.startPtr->data);
            r.startPtr = r.startPtr->next;
            --r.len;
        }
        assert(palindrome == "01234567899876543210");
    }
    {
        const std::string input("AB01234567899876543210XYkljfkajsdkfasd");
        LinkedListElement<char> *inputLL = nullptr;
        auto iterREnd = input.rend();
        for (auto iterR = input.rbegin(); iterR != iterREnd; ++iterR) {
            LL_PushFront(&inputLL, *iterR);
        }
        Result r = LinkedListLongestPalindrome<char>().operator()(inputLL);

        std::string palindrome;
        while (r.len) {
            palindrome.push_back(r.startPtr->data);
            r.startPtr = r.startPtr->next;
            --r.len;
        }
        assert(palindrome == "01234567899876543210");
    }
    {
        const std::string input("AB01234567899876543210ABCDDCBAxyx");
        LinkedListElement<char> *inputLL = nullptr;
        auto iterREnd = input.rend();
        for (auto iterR = input.rbegin(); iterR != iterREnd; ++iterR) {
            LL_PushFront(&inputLL, *iterR);
        }
        Result r = LinkedListLongestPalindrome<char>().operator()(inputLL);

        std::string palindrome;
        while (r.len) {
            palindrome.push_back(r.startPtr->data);
            r.startPtr = r.startPtr->next;
            --r.len;
        }
        assert(palindrome == "01234567899876543210");
    }
}

Tuesday 1 December 2015

Find the Nth Expressible Number of Given Set of Prime Numbers

1. Problem Description
This is a Google interview question for software develop engineer from careercup. Here is the orignal thread,
"
Given a prime set, we call "prime expressible" if a number can be factorized only using given prime numbers. Find n-th big expressible number. 

- pandacoder 20 days ago in United States | Report Duplicate | Flag ".

2. Algorithm
Assume a given set of prime numbers in ascending order, P1, P2, ..., Pm. The expressible number can be any of these: P1^X1 * P2^X2 * ... * Pm^Xm,
    where X1, ... Xm are non-negative integers.
The first M+1 expressible number will be {1, P1, ..., Pm}. If N is not more than M+1, then it is easy to find the value.

Otherwise the expressible number has to be tracked when they grow bigger. A linked-list is used for each prime number to track the expressible number for each prime number as L1, L2, ..., Lm. For any number Eji in Lj has to meeting the following condition,
    - X1, ..., Xj must not be all zero, Xj+1 ... Xm must all be zero.
This guarantees that,
    - The number in L1: P1^X1, where X1 is positive integers
    - L2: P1^X1*P2^X2, where X1 and X2 are positive integers
    - L3: P1^X1*P2^X2*P3^X3, where X1, X2 and X3 are positive integers
    ......
    - Lm: P1^X1*P2^X2*...*Pm^Xm, where X1, X2, ..., Xm are positive integers

At the same time the number is tracked when inserted into L.
    - If N is ranged in [1, m+1], return from {1, P1, ..., Pm}
    - Otherwise,
    - Initialize Nth = 1
    - Starting with {P1}, {P2}, ..., {Pm} for L.
    - Find the smallest value, Es,  of the min heap, MH, that is made of the head of L.
      As MH - {P1, P2, ..., Pm} at the start.
    - Find the corresponding prime number against which linked list Es comes from, Px and Lx
    - Append Es*Px into Lx, Es*Px+1 into Lx+1, ..., Ex*Pm into Lm
    - Remove Es from Lx and increment the Nth
    - Repeat above 4 steps until Nth reaches N.

3. Example
Given prime number sets: {2, 3, 5}
Find the 12th expressible number.

Example 1 - 1
Example 1 - 2


4. C++ Implementation and Test
// ********************************************************************************
// Implementation
// ********************************************************************************
#include <list>
#include <queue>
#include <unordered_map>

struct KeyValuePair
{
    KeyValuePair()
    {}

    KeyValuePair(unsigned long val,
                 unsigned long k,
                 size_t index)
        :value(val), key(k), keyIndex(index)
    {}

    KeyValuePair(const KeyValuePair&) = default;
    KeyValuePair& operator=(const KeyValuePair&) = default;

    unsigned long value = 2;
    unsigned long key = 2;
    size_t keyIndex = 0;
};

struct KeyValuePairComparator
{
    bool operator()(const KeyValuePair& lhs, const KeyValuePair& rhs) {
        return lhs.value > rhs.value;
    }
};

using MinHeap = std::priority_queue<KeyValuePair,
                                    std::vector<KeyValuePair>,
                                    KeyValuePairComparator>;
using ValueMap = std::unordered_map<unsigned long, std::list<unsigned long>>;

unsigned long FindNthExpressibleNumGivenSet(const std::vector<unsigned long>& sortedPrimes,
                                            const unsigned long Nth,
                                            MinHeap& topNums,
                                            ValueMap& nums)
{
    unsigned long numCounter = Nth - 1;

    auto iterStart = sortedPrimes.begin();
    auto iterEnd = sortedPrimes.end();
    unsigned long value;
    while (true) {
        const KeyValuePair kvp = topNums.top();
        nums[kvp.key].pop_front();
        // pop out a value from the priority queue
        // count until the Nth
        --numCounter;
        if (numCounter == 0) {
            return kvp.value;
        }
        // Generate the number and push them into the list
        for (auto iter = iterStart + kvp.keyIndex; iter != iterEnd; ++iter) {
            value = *iter * kvp.value;
            nums[*iter].push_back(value);
        }
        // pop out the value from the priority queue
        // and push its successor from the value list into the priority queue
        topNums.pop();
        topNums.push(KeyValuePair(nums[kvp.key].front(), kvp.key, kvp.keyIndex));
    }

    return 0;
}

unsigned long FindNthExpressibleNumGivenSet(const std::vector<unsigned long>& sortedPrimes,
                                            const unsigned long Nth)
{
    if (sortedPrimes.empty() || Nth == 0) {
        // indicates failure
        return 0;
    }

    if (Nth == 1) {
        return 1;
    }

    if (Nth <= sortedPrimes.size() + 1) {
        return sortedPrimes[Nth - 2];
    }

    MinHeap topNums;
    ValueMap nums;
    auto iterEnd = sortedPrimes.end();
    size_t idx = 0;
    // Starting point:
    // the values: each prime number x 1
    // the priority queue is (prime number, prime number) - <value, key> pair
    for (auto iter = sortedPrimes.begin(); iter != iterEnd; ++iter, ++idx) {
        std::list<unsigned long> values;
        values.push_front(*iter);
        nums[*iter] = values;
        topNums.push(KeyValuePair(*iter, *iter, idx));
    }

    return FindNthExpressibleNumGivenSet(sortedPrimes, Nth, topNums, nums);
}

// ********************************************************************************
// Test
// ********************************************************************************
#include <cassert>
void TestFindNthExpressibleNum()
{
    std::vector<unsigned long> sortedPrimes;
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 0) == 0);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 1) == 0);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 5) == 0);

    sortedPrimes.push_back(2);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 0) == 0);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 1) == 1);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 2) == 2);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 3) == 4);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 4) == 8);

    sortedPrimes.push_back(3);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 0) == 0);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 1) == 1);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 2) == 2);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 3) == 3);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 4) == 4);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 5) == 6);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 6) == 8);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 7) == 9);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 8) == 12);

    sortedPrimes = {3, 5, 7};
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 0) == 0);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 1) == 1);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 2) == 3);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 3) == 5);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 4) == 7);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 5) == 9);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 6) == 15);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 7) == 21);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 8) == 25);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 9) == 27);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 10) == 35);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 11) == 45);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 12) == 49);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 13) == 63);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 14) == 75);
    assert(FindNthExpressibleNumGivenSet(sortedPrimes, 15) == 81);
}