Time and Space Complexity of this Palindrome Algorithm? - algorithm

Can someone tell me the time and space complexity for this algorithm? Basically the function takes in a string and the function must return true if it's a palindrome (same backwards as it is forwards) or false if it is not.
I am thinking it is O(n) for both but please correct me if I am wrong.
function isPalindrome(string) {
var reversing = string.split("").reverse().join("")
return string === reversing
}

Your function has a time and space complexity of O(string.length) because it constructs an array of characters and then a new string with the characters in reverse order, with the same length as the original string. Comparing these strings has the same time complexity.
Note however that this works for single words but not for complete phrases: a phrase that can be read in both directions with the same letters, but not necessarily the same spacing is also a palindrome.
Here is an alternative version:
function isPalindrome(string) {
string = string.replace(/ /g, "");
var reverse = string.split("").reverse().join("");
return string === reverse;
}
This function has the same time and space complexity of O(string.length).

Related

What is the Time and Space complexity of following solution?

Problem statement:
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.
Note:
The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.
Sample test case:
Input:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
Output:
[
"cats and dog",
"cat sand dog"
]
My Solution:
class Solution {
unordered_set<string> words;
unordered_map<string, vector<string> > memo;
public:
vector<string> getAllSentences(string s) {
if(s.size()==0){
return {""};
}
if(memo.count(s)) {
return memo[s];
}
string curWord = ""; vector<string> result;
for(int i = 0; i < s.size(); i++ ) {
curWord+=s[i];
if(words.count(curWord)) {
auto sentences = getAllSentences(s.substr(i+1));
for(string s : sentences) {
string sentence = curWord + ((int)s.size()>0? ((" ") + s) : "");
result.push_back(sentence);
}
}
}
return memo[s] = result;
}
vector<string> wordBreak(string s, vector<string>& wordDict) {
for(auto word : wordDict) {
words.insert(word);
}
return getAllSentences(s);
}
};
I am not sure about the time and space complexity. I think it should be 2^n where n is the length of given string s. Can anyone please help me to prove time and space complexity?
I have also some following questions:
If I don't use memo in the getAllSentences function what will be the
time complexity in this case?
Is there any better solution than this?
Let's try to go through the algorithm step by step but for specific wordDict to simplify the things.
So let wordDict be all the characters from a to z,
wordDict = ["a",..., "z"]
In this case if(words.count(curWord)) would be true every time when i = 0 and false otherwise.
Also, let's skip using memo cache (we'll add it later).
In the case above, we just got though string s recursively until we reach the end without any additional memory except result vector which gives the following:
time complexity is O(n!)
space complexity is O(1) - just 1 solution exists
where n - lenght of s
Now let's examine how using memo cache changes the situation in our case. Cache would contain n items - size of our string s which changes space complexity to O(n). Our time is the same since every there will be no hits by using memo cache.
This is the basis for us to move forward.
Now let's try to find how the things are changed if wordDict contains all the pairs of letters (and length of s is 2*something, so we could reach the end).
So, wordDict = ['aa','ab',...,'zz']
In this case we move forward with for 2 letters instead of 1 and everything else is the same, which gives us the following complexity withoug using memo cache:
time complexity is O((n/2)!)
space complexity is O(1) - just 1 solution exists
Memo cache would contain (n/2) items, giving a complexity of O(n) which also changes space complexity to O(n) but all the checks there are of different length.
Let's now imagine that wordDict contains both dictionaries we mentioned before ('a'...'z','aa'...'zz').
In this case we have the following complexity without using memo cache
time complexity is O((n)!) as we need to check the case for i=0 and i=1 which roughly doubles the number of checks we need to do for each step but on the other size it reduces the number of checks we have to do later since we move forward by 2 letters instead of one (this is the trickiest part for me).
Space complexity is ~O(2^n) since every additional char doubles the number of results.
Now let's think of the memo cache we have. It would be usefull for every 3 letters, because for example '...ab c...' gives the same as '...a bc...', so it reduces the number of calculations by 2 at every step, so our complexity would be the following
time complexity is roughly O((n/2)!) and we need O(2*n)=O(n) memory to store the memo. Let's also remember that in n/2 expression 2 reflects the cache effectiveness.
space complexity is O(2^n) - 2 here is a charateristic of the wordDict we've constructed
These were 3 cases for us to understand how the complexity is changing depending of the curcumstances. Now let's try to generalize it to the generic case:
time complexity is O((n/(l*e))!) where l = min length of words in wordDict, e - cache effectiveness (I would assume it 1 in general case but there might bt situations where it's different as we saw in the case above
space complexity is O(a^n) where a is a similarity of words in our wordDict, could be very very roughly estimated as P(h/l)=(h/l)! where h is max word length in a dictionary and l is min word length as (for example, if wordDict contains all combinations of up 3 letters, this gives us 3! combinations for every 6 letters)
This is how I see your approach and it's complexity.
As for improving the solution itself, I don't see any simple way to improve it. There might be an alternative way to divide the string in 3 parts and then processing each part separately but it would definitely work if we could get rid of searching the results and just count the number of results without displaying them.
I hope it helps.

Understanding this Time Complexity issue (Cracking the coding interview)

Question:
String joinWords(String[] words) {
String sentence = "";
for (String w : words) {
sentence = sentence + w;
}
return sentence;
}
So the solution of this is O(xn^2)
From what I understand, for every iteration, the amount of letters in the variable 'sentence' increases by one. Which, I think, will be O(n^2)?
Is the 'x' just the number of letters in 'words' array?
Answer: On each concatenation, a new copy of the string is created, and the two strings are copied over, character by character. The first iteration requires us to copy x characters. The second iteration requires copying 2x characters. The third iteration requires 3x, and so on. The total time therefore is O(x + 2x + ... x nx). This reduces to O(xn^2)
The answer is wrong in two ways. O(xn^2) doesn't exist. In big O you drop all constants. It would be O(n^2) if that was right (it isn't).
The next part depends on language, the implementation of + on strings and the string class itself. If the string class is mutable, then + should be O(n) assuming it has a decent implementation (doesn't cause a reallocatiion and copy on each use of +). If the string class is immutable, it depends on how the String is implemented- does it use a single character buffer for all data, or can it handle multiple character pointers in an ordered list? Of course even on the worst implementation it wouldn't be O(n^2), more like O(2n) which is O(n) (2 copies per iteration). Anyone giving me an answer of O(n^2) would be marked wrong. But really any modern String class implementation would be O(n) without a constant and be pretty much O(n*l) in space (where n is the number of words and l is the average word length).
class String {
String base; //used for appended strings
String additional; //used for appended strings
char baseData[]; //used for pure strings
String(String base, String additional) {
this.base = base;
this.additional = additional;
}
operator + (String newString) {
return new String(this, newString);
}
//As an example of how this works
int length() {
if(base != null) {
return base.length()+additional.length(); //This can be cached, and should be for efficiency.
}
else {
return baseData.length;
}
}
}
Notice that + is O(1). Yes, I know Java doesn't have operator overloading, the function is there to show how its implemented.

The earliest shortest match location

Assume you have a function matchWithMagic that returns a Boolean value for a given string. You don't know any detail how it do it but you know the result of true means "Match". Assuming the complexity of this function is linear in time and space to the size of input string.
Now the question is to implement a function that for a given string, that will return an pair of integers, namely pos and len such that matchWithMagic(substring(inputstring,pos,len)) matches and pos is the least number that this can be true (earliest match). When pos is known, len is the least number for a match (shortest match, with a lower priority). There are no requirement on efficiency but the answer should includes performance analyse.
For example, suppose the magic function return true for input strings contains "Good Guy!" or "Bad Guy!" your function should return pos=5,len=8 for "Good Bad Guy!".
Preferred languages is C/C++/Java/JavaScript/C#/Basic, but other languages are OK.
UPDATE
A trivial answer has now been posted. I hope a more efficient solution could appear.
Given that the function is black-box, I'm not sure you can do better than this:
for (int pos = 0:n)
for (int len = 0:(n-pos))
if (matchWithMagic(substring(inputstring,pos,len)))
return {pos, len};
return null;
Which I believe is O(n^3) (assuming matchWithMagic is O(n)).

Efficient way to verify if a string is a rotated palindrome?

A rotated palindrome is like "1234321", "3432112".
The naive method will be cut the string into different pieces and concate them back and see if the string is a palindrome.
That would take O(n^2) since there are n cuts and for each cut we need O(n) to check if the string is a palindrome.
I'm wondering if there's a better solution than this.
I guess so, please advice.
Thanks!
According to this wikipedia article it is possible for each string S of length n in time O(n) compute an array A of the same size, such that:
A[i]==1 iff the prefix of S of length i is a palindrome.
http://en.wikipedia.org/wiki/Longest_palindromic_substring
The algorithm should be possible to find in:
Manacher, Glenn (1975), "A new linear-time "on-line" algorithm for
finding the smallest initial palindrome of a string"
In other words we can check which prefixes of the string are palindromes in linear time. We will use this result to solve the proposed problem.
Each (non-rotating) palindrome S has the following form S = psxs^Rp^R.
Where "x" is the center of the palindrome (either empty string or one letter string),
"p" and "s" are (possibly empty) strings and "s^R" means "s" string reversed.
Each rotating palindrome created from this string has one of the two following forms (for some p):
sxs^Rp^Rp
p^Rpsxs^R
This is true, because you can choose if to cut some substring before or after the middle of the palindrome and then paste it on the other end.
As one can see the substrings "p^Rp" and "sxs^R" are both palindromes, one of then of even length and the other on odd length iff S is of odd length.
We can use the algorithm mentioned in the wikipedia link to create two arrays A and B. The array A is created by checking which prefixes are palindromes and B for suffixes. Then we search for a value i such that A[i]==B[i]==1 such that either prefix or suffix has even length. We will find such index iff the proposed string is a rotated palindrome and the even part is the "p^Rp" substring, so we can easily recover the original palindrome by moving half of this string to the other end of the string.
One remark to the solution by rks, this solution doesn't work, as for a string S = 1121 it will create string 11211121 which has palindrome of length longer or equal than the length of S, but it is not a rotated palindrome. If we change the solution such that it checks whether there exist a palindrome of length equal to length of S, it would work, but i don't see any direct solution how to change the algorithm searching for longest substring in such a way that it would search for substring of fixed length (len(S)).
(i didn't write this as a comment under the solution as i'm new to Stackoverflow and don't have enough reputation to do so)
Second remark -- I'm sorry not to include the algorithm of Manacher, if someone has link to either the idea of the algorithm or some implementation please include it in the comments.
Concatenate the string to itself, then do the classical palindrome research in the new string. If you find a palindrome whose length is longer or equal to the length of your original string, you know your string is a rotated palindrome.
For your example, you would do your research in 34321123432112 , finding 21123432112, which is longer than your initial string, so it's a rotated palindrome.
EDIT: as Richard Stefanec noted, my algorithm fails on 1121, he proposed that we change the >= test on the length by =.
EDIT2: it should be noted than finding a palindrome of a given size isn't obviously easy. Read the discussion under Richard Stefanec post for more information.
#Given a string, check if it is a rotation of a palindrome.
#For example your function should return true for “aab” as it is a rotation of “aba”.
string1 = input("Enter the first string")
def check_palindrome(string1):
#string1_list = [word1 for word1 in string1]
#print(string1_list)
string1_rotated = string1[1::1] + string1[0]
print(string1_rotated)
string1_rotated_palindrome = string1_rotated[-1::-1]
print(string1_rotated_palindrome)
if string1_rotated == string1_rotated_palindrome:
return True
else:
return False
isPalindrome = check_palindrome(string1)
if(isPalindrome):
print("Rotated string is palindrome as well")
else:
print("Rotated string is not palindrome")
I would like to propose one simple solution, using only conventional algorithms. It will not solve any harder problem, but it should be sufficient for your task. It is somewhat similar to the other two proposed solutions, but none of them seems to be concise enough for me to read carefully.
First step: concatenate the string to itself (abvc - > abvcabvc) as in all other proposed solutions.
Second step: do Rabin-Karp precalculation (which uses rolling hash) on the newly obtained string and its reversed.
Third step: Let the string be with length n. For each index iin 0...n-1 check if the substring of the doubled string [i, i + n - 1] is palindrome in constant time, using the Rabin-Karp precalculations (basically the obtained value in for the substring in the forward and the reversed direction should be equal).
Conclusion: if third step found any palindrome - then the string is rotated palindrome. If not - then it is not.
PS: Rabin Karp uses hashes, and collisions are possible even for non-coinciding strings. Thus it is a good idea to make verifying brute force check for equality if such is induced by the hash checks. Still if the hash functions used in the Rabin Karp are good, the amortized speed of the solution should remain O(n).
you can add the same pattern to the end of the original pattern. For example the pattern is 1234321, then you can add the same pattern to the end 12343211234321. After to do this, you can use the KMP or other substring match algotithms to find the string you want. if match, return ture.

String Tiling Algorithm

I'm looking for an efficient algorithm to do string tiling. Basically, you are given a list of strings, say BCD, CDE, ABC, A, and the resulting tiled string should be ABCDE, because BCD aligns with CDE yielding BCDE, which is then aligned with ABC yielding the final ABCDE.
Currently, I'm using a slightly naïve algorithm, that works as follows. Starting with a random pair of strings, say BCD and CDE, I use the following (in Java):
public static String tile(String first, String second) {
for (int i = 0; i < first.length() || i < second.length(); i++) {
// "right" tile (e.g., "BCD" and "CDE")
String firstTile = first.substring(i);
// "left" tile (e.g., "CDE" and "BCD")
String secondTile = second.substring(i);
if (second.contains(firstTile)) {
return first.substring(0, i) + second;
} else if (first.contains(secondTile)) {
return second.substring(0, i) + first;
}
}
return EMPTY;
}
System.out.println(tile("CDE", "ABCDEF")); // ABCDEF
System.out.println(tile("BCD", "CDE")); // BCDE
System.out.println(tile("CDE", "ABC")); // ABCDE
System.out.println(tile("ABC", tile("BCX", "XYZ"))); // ABCXYZ
Although this works, it's not very efficient, as it iterates over the same characters over and over again.
So, does anybody know a better (more efficient) algorithm to do this ? This problem is similar to a DNA sequence alignment problem, so any advice from someone in this field (and others, of course) are very much welcome. Also note that I'm not looking for an alignment, but a tiling, because I require a full overlap of one of the strings over the other.
I'm currently looking for an adaptation of the Rabin-Karp algorithm, in order to improve the asymptotic complexity of the algorithm, but I'd like to hear some advice before delving any further into this matter.
Thanks in advance.
For situations where there is ambiguity -- e.g., {ABC, CBA} which could result in ABCBA or CBABC --, any tiling can be returned. However, this situation seldom occurs, because I'm tiling words, e.g. {This is, is me} => {This is me}, which are manipulated so that the aforementioned algorithm works.
Similar question: Efficient Algorithm for String Concatenation with Overlap
Order the strings by the first character, then length (smallest to largest), and then apply the adaptation to KMP found in this question about concatenating overlapping strings.
I think this should work for the tiling of two strings, and be more efficient than your current implementation using substring and contains. Conceptually I loop across the characters in the 'left' string and compare them to a character in the 'right' string. If the two characters match, I move to the next character in the right string. Depending on which string the end is first reached of, and if the last compared characters match or not, one of the possible tiling cases is identified.
I haven't thought of anything to improve the time complexity of tiling more than two strings. As a small note for multiple strings, this algorithm below is easily extended to checking the tiling of a single 'left' string with multiple 'right' strings at once, which might prevent extra looping over the strings a bit if you're trying to find out whether to do ("ABC", "BCX", "XYZ") or ("ABC", "XYZ", BCX") by just trying all the possibilities. A bit.
string Tile(string a, string b)
{
// Try both orderings of a and b,
// since TileLeftToRight is not commutative.
string ab = TileLeftToRight(a, b);
if (ab != "")
return ab;
return TileLeftToRight(b, a);
// Alternatively you could return whichever
// of the two results is longest, for cases
// like ("ABC" "BCABC").
}
string TileLeftToRight(string left, string right)
{
int i = 0;
int j = 0;
while (true)
{
if (left[i] != right[j])
{
i++;
if (i >= left.Length)
return "";
}
else
{
i++;
j++;
if (i >= left.Length)
return left + right.Substring(j);
if (j >= right.Length)
return left;
}
}
}
If Open Source code is acceptable, then you should check the genome benchmarks in Stanford's STAMP benchmark suite: it does pretty much exactly what you're looking for. Starting with a bunch of strings ("genes"), it looks for the shortest string that incorporates all the genes. So for example if you have ATGC and GCAA, it'll find ATGCAA. There's nothing about the algorithm that limits it to a 4-character alphabet, so this should be able to help you.
The first thing to ask is if you want to find the tilling of {CDB, CDA}? There is no single tilling.
Interesting problem. You need some kind of backtracking. For example if you have:
ABC, BCD, DBC
Combining DBC with BCD results in:
ABC, DBCD
Which is not solvable. But combining ABC with BCD results in:
ABCD, DBC
Which can be combined to:
ABCDBC.

Resources