Interview Question


Country: United States




Comment hidden because of low score. Click to expand.
0
of 0 vote

My algorithm iterates the string from left to right once and try to match the palindromes from inside out, adding the matches to a std::unordered_set (hash set). Written in C++.

Output for "civic":

ivi
v
i
civic
c

Output for "aba":

aba
b
a

Code:

#include <iostream>
#include <unordered_set>
#include <string>
#include <vector>

std::vector<std::string> find_palindromes(const std::string& s, size_t center) {
	std::vector<std::string> palindromes;
    
    // Add current char
    palindromes.emplace_back(1, s[center]);
    
	size_t left = center;
	size_t right = center;
    
	while (left != -1 && right < s.size()) {
		if (s[left] != s[right]) {
            // Try to match one char to the left
            if (left != -1) {
                if (s[left--] == s[right])
                    palindromes.emplace_back(s.substr(left, right - left + 1));
            }
            continue;
        }
        
        // Only add palindrome if length is bigger than 1
        // since we already added it before the loop.
        if (left != right) {
            palindromes.emplace_back(s.substr(left, right - left + 1));
            ++right;
        } else {
            --left;
        }
    }
    
	return palindromes;
}

std::unordered_set<std::string> distinct_palindromes(const std::string& s) {
	if (s.empty())
		return std::unordered_set<std::string>();
    
    std::unordered_set<std::string> result;
	for (size_t i = 0; i < s.size(); ) {
		auto palindromes = find_palindromes(s, i);
		
		// Insert on the set, keeping track of the longest
		size_t longest = 0;
		for (const std::string& p : palindromes) {
			result.insert(p);
			longest = std::max(longest, p.size());
		}
		
		// Skip as much as the longest palindrome found
        if (longest > i)
            i += longest - i;
        else
            ++i;
	}
	
    return result;
}

int main() {
	std::unordered_set<std::string> civic = distinct_palindromes("civic");
	for (const std::string& p : civic) {
		std::cout << p << std::endl;
	}
	std::cout << "--" << std::endl;
	std::unordered_set<std::string> aba = distinct_palindromes("aaaa");
	for (const std::string& p : aba) {
		std::cout << p << std::endl;
	}
}

- Diego Giagio December 11, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
-1
of 1 vote

could you plz provide your code in java..as i am a java coder

- justhack4fun688 December 12, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

Your code gives one palindromic substring for "aa" but in actual their are two :a,aa

- justhack4fun688 December 12, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

The reason it doesnt work aa is Diego's code considers only odd length palindromes (based on his convention of picking a char as a center and branching left and right). For even length palindormes, there is no single center. for eg maam. Center is aa, consider first a as left-center and second a as right-center

Fix to his code would be considering each char not only as center but also as a left-center

- Anonymous December 13, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

Good code, but few comments on style.
there is no need for the final

if (s[left] == s[right]) {
		palindromes.push_back(s.substr(left, right - left + 1));
	}

after the while loop. the condition can be changed to (left>=0 instead of left!=0) to remove this code.
and we should not return vector or sets from a function. huge copying involved. pass by reference. this is looked for esp in an itnerview wehn writgin c++ code
otherwise good code.

- Anonymous December 13, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@justhack4fun688 and @Anonymous1: Thanks for pointing that out. I fixed the code and it should work now.

@Anonymous2: The condition of the while loop cannot be changed to left >= 0 simply because left is of type size_t. This is an unsigned type and left >= 0 will always evaluate to true. Yes, we should return vectors or sets by value when it makes sense[1] (like this code). C++03 compilers use RVO and C++11 have even more room for optimization for copy elision.[2]

[1] http://en.wikipedia.org/wiki/Return_value_optimization
[2] http://stackoverflow.com/questions/3350385/how-to-return-an-object-in-c

- Diego Giagio December 14, 2013 | Flag
Comment hidden because of low score. Click to expand.
-1
of 1 vote

@Diego Giagio It still give wrong answer for string "abba"

- justhack4fun688 December 15, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@justhack4fun688 You are correct. I fixed the code. It's simpler and worked with everything I tried. Can you please check again?

- Diego Giagio December 15, 2013 | Flag
Comment hidden because of low score. Click to expand.
-1
of 1 vote

@Diego Giagio sorry to say but still wrong for strings like "aaaa".

- justhack4fun688 December 15, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@justhack4fun688 Wow, thanks. Now it is. Can you check again? :-)

- Diego Giagio December 15, 2013 | Flag
Comment hidden because of low score. Click to expand.
-1
of 1 vote

@Diego Giagio What is complexity of your purposed algorithm?

- justhack4fun688 December 15, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@justhack4fun688 O(n^2) time worst case. I've heard it's possible to have O(n) but it's a bit more complicated and you have to use Manacher’s Algorithm.

- Diego Giagio December 15, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@justhack4fun688 I just did some performance improvements by skipping indexes of already found palindromes. It much faster on the average case, but it's still O(n^2) worst case.

- Diego Giagio December 15, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@Diego Giagio the code is better undoubtdly. but last problem is space.How much is its space complexity.? analyse it

- justhack4fun688 December 16, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@justhack4fun688 The worst case for space is a string with repeated chars, like 'aaaaaaaaa'. Which gives 'a', 'aa', 'aaa', 'aaaa', etc. The formula to calculate the worst case is (n * (n+1))/2 where n is the length of the input string. For a string with 9 repeated 'a's it gives (9 * 10) / 2 = 45. So I would say the worst case space is O(n*(n+1)/2).

- Diego Giagio December 16, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@Diego Giagio YA. i know it..its O(n^2). Now for a string of length 10^5 it will take huge memory.Dont u think so?

- justhack4fun688 December 16, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@justhack4fun688 Of course it will, it's a huge string. What's you suggestion?

I could save on the set only the pairs of the begin and end indexes of each palindrome on the original string. That would save a lot of memory, but I chose not to for this exercise.

- Diego Giagio December 16, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

Not answering the question.

An O(n^2) algorithm is a great response in an interview for this question.

When the interviewer asks for a better algorithm, it does not mean you have done bad. They are looking to see how you think, when trying to come up with a better algo, and are now WANTING or EXPECTING a better algorithm.

- Anonymous December 11, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

gah. typo: "...and are NOT WANTING or EXPECTING..."

- Anonymous December 11, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

Here's a Java code that seems to work :

import java.util.ArrayList;

public class PalindromeFinder {
	
	public static void main (String[] args) {
		PalindromeFinder pf = new PalindromeFinder();
		System.out.println(pf.countDistinctPalindromes("gfdfhweufuefk"));
	}
	
	public int countDistinctPalindromes(String s) {
		for (int i = 0; i < s.length(); ++i) {
			findPalindromes(s, i);
		}
		return found.size();
	}

	private void findPalindromes(String s, int i) {
		int j = i,k = i;
		while ((j >= 0) && (k < s.length()) && (s.charAt(j) == s.charAt(k))) {
			report(s.substring(j--, ++k));
		}
		
		if (i == 0) {
			return;
		}
		
		k = i;
		j = i-1;
		
		while ((j >= 0) && (k < s.length()) && (s.charAt(j) == s.charAt(k))) {
			report(s.substring(j--, ++k));
		}
		
	}
	
	private void report(String s) {
		if (!wasFound(s)) {
			found.add(s);
//			System.out.println(s);
		}
	}
	
	private boolean wasFound(String s) {
		return found.contains(s);
	}

	private ArrayList<String> found = new ArrayList<String>();
}

The printing of all palindromes was commented out, but you can comment it in again.

Its compexity is actually

p^2

where

p

is the number of (not necessarily unique) palindromes in the given string, S, which itself can be

p = O(n^2)

where

n

is the length of S. (worst case achieved when s consist of just one letter, repeating itself), but it's only because I used Java-supplied List, instead of a ballanced tree or even a Trie to keep track of the already found palindromes. This would improve the running time to

O(p log p) =(worst case) O(n^2 log n)

.

- Elad December 14, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
-1
of 1 vote

@Elad Your algorithm seems right.But tooo slow for larger strings.

- justhack4fun688 December 15, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@justhack4fun688

you mean the [p log p] is too slow? I said the p^2 is just because lack of an API for a ballanced tree in java.

I don't think you can do better if you want DISTINCT palindromes..
and actually, it's slightly better than p log p, cause the p inside the log is the number of distinct palindromes, and the p outside is of all of them.
Now that I think of it, the comparison itself is not exactly O(1), but I don't know if it could practically effect the runtime complexity..

do can you describe a sketch of an O(n^2) algorithm? I can't think of a way to do that..

- Elad December 16, 2013 | Flag


Add a Comment
Name:

Writing Code? Surround your code with {{{ and }}} to preserve whitespace.

Books

is a comprehensive book on getting a job at a top tech company, while focuses on dev interviews and does this for PMs.

Learn More

Videos

CareerCup's interview videos give you a real-life look at technical interviews. In these unscripted videos, watch how other candidates handle tough questions and how the interviewer thinks about their performance.

Learn More

Resume Review

Most engineers make critical mistakes on their resumes -- we can fix your resume with our custom resume review service. And, we use fellow engineers as our resume reviewers, so you can be sure that we "get" what you're saying.

Learn More

Mock Interviews

Our Mock Interviews will be conducted "in character" just like a real interview, and can focus on whatever topics you want. All our interviewers have worked for Microsoft, Google or Amazon, you know you'll get a true-to-life experience.

Learn More