How to compare Palindromes - javascript

Not sure what i did wrong. I'm trying to compare 2 strings to see if they are Palindromes
function palindrome(str) {
var rem = str.replace(/\s/g, "");
var a = rem.split("");
var b = a.reverse();
for(var i = 0; i < rem.length; i++){
if (b[i] == a[i]){
return true;
}
return false;
}
}
palindrome("not a doctor"); //this should show false, but it's showing true

The reverse method transposes the elements of the calling array object in place, mutating the array, and returning a reference to the array.
Source
That's why you get true. Seems your a and b variables point to the same object.
By the way, your approach seems somewhat flawed as others have pointed out bellow. So, a better version of solution to your task could be (not properly tested):
function isPalindrome(input) {
var str = input.replace(/\s/g, "").toLowerCase();
var left = 0, right = str.length - 1;
while( left <= right) {
if(str[left] !== str[right]) return false;
left++; right--;
}
return true;
}
console.log(isPalindrome("your string")); // false
console.log(isPalindrome("Drawn onward")); //true
The idea is that you compare the corresponding symbols on both ends of your modified (without spaces and lowercased) string. If they don't match at some point, so this is not a palindrome.

There are a few problems with your code.
Problem 1: You are using reverse method which mutate the original array (See the docs on reverse). So variables a and b would have the same value, that is the reversed string of the original array. What you can do instead is create a fresh new array and assign it to the variable b like so:
var b = [].concat(a).reverse();
Problem 2: You forgot to check ALL of the letters in the string. You are returning your function way too early. For example, for the input string 'aada', which is not a palindrome it will return true. This is because, your function exits as soon as evaluates the similarity of the first string of both arrays. To fix this you could do something like this:
function palindrome(str) {
var rem = str.replace(/\s/g, "");
var a = rem.split("");
var b = [].concat(a).reverse();
for(var i = 0; i < rem.length; i++){
if (b[i] !== a[i]){
return false;
}
}
return true
}
You can even further optimise your function like this:
function palindrome(str) {
const len = str.length;
const halfLen = len/2;
for (let i = 0; i < halfLen; i++) {
if (str[i] !== str[len - 1 - i]) return false;
}
return true;
}
Palindrome strings read the same backwards and forwards, So you can make a comparison of the first and the last, second to second last etc until you reach the middle character.
Hope this helps.

As others have said, your a and b points to same object, so you need to clone it first.
Also you must not return true immediately, better way is to check for inequality and return true after the whole cycle ends.
function palindrome(str) {
var rem = str.replace(/\s/g, "");
var a = rem.split("");
var b = a.slice().reverse();
console.log(a, b);
for(var i = 0; i < rem.length; i++){
if (b[i] != a[i]){
return false;
}
}
return true;
}
console.log(palindrome("lol 1"));

Your code had two problems.
As noted by #curveball, your a and b variables are references to the same objects, as reverse modifies the original array.
Additionally, you are returning true as soon as the first element in the a array is equal to the first element of the b array. You must return false as soon as one element is different from another. But can only return true after comparing all the elements in the array.
function palindrome(str) {
var rem = str.replace(/\s/g, "");
var a = rem.split("");
var b = a.slice().reverse();
for(var i = 0; i < rem.length; i++){
if (b[i] !== a[i]){
return false;
}
}
return true;
}
palindrome("not a doctor");
Additionally, an alternative (and typical) algorithm for checking whether a string is a palindrome would be to compare (after removing blanks) first position to last one, second position to second from the end, etc. That way it is not necessary to clone (and reverse) the data.

Related

Word Break algorithm

I'm trying to implement the "Word Break" algorithm.
Problem:
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
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.
Example:
Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".
My solution:
var wordBreak = function(s, wordDict) {
if(!wordDict || wordDict.length === 0)
return false;
while(wordDict.length > 0 || s.length > 0) {
const word = wordDict.shift();
const index = s.indexOf(word);
if(index === -1) {
return false;
}
s = s.substring(0, index) + s.substring(index+word.length, s.length);
}
return s.length === 0 && wordDict.length === 0 ? true : false;
};
It works for the example (input) above. However it fails for the input below.
Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
Note that you are allowed to reuse a dictionary word.
How can I keep track of words that I already eliminate and check it at the end. This input above, the remaining s string contains "apple" which is in the word dictionary, so the output should be true.
Thanks
A simple Javascript solution.
This loops through the wordDict array and checks if each word exist in the str. If it doesn't that is when the indexOf the word return -1, the function returns false. However, if the words in the wordDict array are in the string, it returns true at the end of the for loop.
const wordBreak =(str, wordDict)=>{
if (!wordDict || wordDict.length === 0) return false
for(let i=0; I<wordDict.length; i++){
const dictIndex = str.indexOf(wordDict[i])
if(dictIndex === -1){
return false
}
}
return true
}
This is an interesting problem I met two years ago in a different context, i.e., query tokenization. In my case, the number of words in the dictionary was in the order of several million, therefore a recursive approach looking each time for a different word of the dictionary was not practicable. Furthermore, I needed to apply dynamic programming to solve the task for strict efficiency reasons.
First of all, I suggest you to use the AhoCorasick algorithm to find the words within your search string. The algorithm looks for an arbitrary number of patterns in a string in linear time in the length of the string regardless of the number of patterns to find (no more number of words times length of the string operation, indeed each find of a word in a string needs to scan the entire string..).
Luckily, I found a javascript implementation of the algorithm here.
Using the code linked above and dynamic programming to track the words appearing in your string, I wrote the following javascript solution:
function wordBreak(s, wordDict) {
const len = s.length;
const memoization_array_words = new Array(len).fill(null);
const memoization_array_scores = new Array(len).fill(0);
const wordScores = {};
wordDict.forEach(function(word) {
wordScores[word] = 1
});
automata = new AhoCorasick(wordDict);
results = automata.search(s);
results.forEach(function(result) {
// result[0] contains the end position
// result[1] contains the list of words ending in that position
const end_pos = result[0];
result[1].forEach(function(word) {
const prev_end_pos = end_pos - word.length;
const prev_score = (prev_end_pos == -1) ? 0 : memoization_array_scores[prev_end_pos];
const score = prev_score + wordScores[word];
if (score > memoization_array_scores[end_pos]) {
memoization_array_words[end_pos] = word;
memoization_array_scores[end_pos] = score;
}
});
});
if (memoization_array_words[len-1] == null) {
return false;
}
solution = []
var pos_to_keep = len - 1;
while (pos_to_keep >= 0) {
const word = memoization_array_words[pos_to_keep];
solution.push(word);
pos_to_keep -= word.length;
}
return solution.reverse()
}
where memoization_array_words and memoization_array_scores are filled left to right when we meet a word occurring after a previous one or at the beginning of the string s. The code should be autoesplicative, but if you need any explanation write me a comment, please.
As a plus, I associated a score to each word (here is 1 for simplicity) that allows you to distinguish between the different solutions. For instance, if you associate to each word an importance score, you will end up with the tokenization with the greatest score. In the code above, the tokenization with the highest number of words.
Extended version: I testing over the wordDict with some if there is one of the worde that beginns at the test-string (indexOf==0). If so I shorten the string about the length of the word and call the function recursivly with the shortened string. Otherwise the string is not splitable and I return false. I go this way on till an error occurs or the length of the string is 0 and I win because everything goes allright.
Remark: The error when the WordBreak is not clearly like with s= "cars" wordDict = ["car","ca","rs"] is now fixed. For this I calling in the some-methode the algorithm recursivly. So if one way stops before ending I go backwards and search for alternatives till I found one or there is no possibility left.
Remarks to; array.some
In an array.forEach there can't used a break without using some ugly tricks (like try...catch and throwing an error), so I could use the classic variant of the for-loop. But there exists the array.some method this loops like a forEach-loop but there had only one of the elements to be return true so the result is true.
Example:
const array = [1, 2, 3, 4, 5];
// checks whether an element is even
const even = (element) => element % 2 === 0;
console.log(array.some(even));
Here is the code of the working algorithm.
var wordBreak = function(s, wordDict) {
if (!wordDict || wordDict.length === 0) return false;
while (s.length > 0) {
let test = wordDict.some( (word,index) => {
if (s.indexOf(word)===0) {
s_new = s.substr(word.length);
return wordBreak(s_new, wordDict);
}
});
if (!test ) return false;
s=s_new;
}
if (s.length === 0) return true;
}
s = "leetcode"; wordDict = ["leet", "code"];
console.log(wordBreak(s, wordDict));
s = "applepenapple"; wordDict = ["apple", "pen"];
console.log(wordBreak(s, wordDict));
s= "cars"; wordDict = ["car","ca","rs"];
console.log(wordBreak(s, wordDict));
function wordBreak(dict, str){
if (!str){
return true;
}
for (const word of dict){
if (str.startsWith(word)){
return wordBreak(dict, str.substring(word.length, str.length))
}
}
return false;
}
You could also probably optimize the loop over dict by pre-sorting the array and using binary search, but hopefully this gets the point across.
If you'd be looking for a Dynamic Programming solution, we'd use an array for recording, and then we'd loop through and keep track of the word.
This'll pass through in JavaScript:
const wordBreak = function(s, wordDict) {
const len = s.length
const dp = new Array(len + 1).fill(false)
dp[0] = true
for (let i = 1; i < len + 1; i++) {
for (let j = 0; j < i; j++) {
if (dp[j] === true && wordDict.includes(s.slice(j, i))) {
dp[i] = true
break
}
}
}
return dp[s.length]
}
In Python, we would have used a list (which is similar to an array of JavaScript) with the same size as our string:
class Solution:
def wordBreak(self, s, words):
dp = [False] * len(s)
for i in range(len(s)):
for word in words:
k = i - len(word)
if word == s[k + 1:i + 1] and (dp[k] or k == -1):
dp[i] = True
return dp[-1]
Similarly in Java, we'd have used a boolean[]:
public final class Solution {
public static final boolean wordBreak(
String s,
List<String> words
) {
if (s == null || s.length() == 0) {
return false;
}
final int len = s.length();
boolean[] dp = new boolean[len];
for (int i = 0; i < len; i++) {
for (int j = 0; j <= i; j++) {
final String sub = s.substring(j, i + 1);
if (words.contains(sub) && (j == 0 || dp[j - 1])) {
dp[i] = true;
break;
}
}
}
return dp[len - 1];
}
}
Here is LeetCode's DP solution:
public class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> wordDictSet=new HashSet(wordDict);
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for (int i = 1; i <= s.length(); i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
References
For additional details, please see the Discussion Board which you can find plenty of well-explained accepted solutions in there, with a variety of languages including efficient algorithms and asymptotic time/space complexity analysis1, 2.

JavaScript Regex - test for sets of four non-duplicate characters

I am trying to use Regex to test if a certain string contains only sets of four non-duplicate characters.
For example I would like to test string
acbdbcaddacb
which would return true as it can return
acbd
bcad
dacb
i.e. sets of four characters which have no duplicates even though the entire string does.
I have tried the following regex which does not work for example and I am not sure why:
/^(?:(?:([a-d])(?!.{0,2}\1))(?:([a-d])(?!.{0,1}\1))(?:([a-d])(?!\1))[a-d])+$/
Any solutions?
Thank you
You're close. Your current regex is only checking if the 2nd - 4th letters in each group match the 1st. I believe /^(?:(?:([a-d])(?!.{0,2}\1))(?:([a-d])(?!.{0,1}\1|\2))(?:([a-d])(?!\1|\2|\3))[a-d])+$/ should work... or at least it's getting closer to correct I'm not sure if I left out some edge cases but it seems to be working for my test strings
Try this :
function check(str) {
var len = str.length; // check string length
if (len % 4 == 0) { // pass if divided by 4 == true
var arr = str.match(/.{4}/g); // make the in array
var res = [];
for (i = 0; i < arr.length; i++) {
if (arr[i].match(/^(?:([a-zA-Z])(?!.*\1))*$/)) {
res.push(arr[i]); // push if passed regex
}
}
if (arr.length === res.length) { // if they same that means true
console.log("true");
} else {
console.log("false");
}
} else {
console.log("false");
}
}
var str1 = "acbdbcaddacb";
check(str1); // true
var str2 = "aabbccdd";
check(str2); // false
var str3 = "abcde";
check(str3); // false
var str4 = "abcdabcdabcdabcd";
check(str4); // true
var str5 = "abcdabcdabcdabc4";
check(str5); // false

Why Javascript console.log result is undefined while reversing the string?

var a = "gsdgtrshghf";
function reverseString(strr){
if (!strr.length){
var result="";
for(var i=strr.length;i>0;i++){
var a=strr.chatAt(i);
result+=a;
}
}return result;
}
console.log(reverseString(a))
When I tried to run it it returned me "undefined". I wonder what's the problem here.
The main reason is you are declaring var result="" and returning from outside of if(so it become undefined as its scope is only inside if statement) and other errors areas mention in comments you have a typo, charAt not chatAt. You can also simply use strr[i] to get the char. Also, you should do i-- and i >= 0 if you start at strr.length, otherwise for loop is immediately completed at the condition check. Check the below code.
var a = "gsdgtrshghf";
function reverseString(strr){
var result="";
if (strr.length){
for(var i=strr.length-1;i>=0;i--){
var a=strr.charAt(i);
result+=a;
}
}
return result;
}
console.log(reverseString(a))
Have a look:
var a = "gsdgtrshghf";
function reverseString(strr) {
var result = "";
if (strr.length != null) {
for (var i = strr.length - 1; i >= 0; i--) {
var a = strr.charAt(i);
result += a;
}
}
return result;
}
console.log(reverseString(a));
// Better
const reverse = str => Array.from(str).reverse().join('');
console.log(reverse('foo 𝌆 bar mañana mañana'));
Explanation
It's charAt(i) not chatAt(i)
Loop should start from length - 1 and end at 0 and i should be decremented
And finally declare the variable outside of if
i.e for(var i = strr.length - ; i >= 0; i--){
not for(var i=strr.length;i>0;i++){
Better yet, use combo of Array.from(str).reverse().join(''), as it even works with Unicode characters, as pointed out in comments by gaetanoM

How can I compare two shuffled strings?

I have the following two strings:
var str1 = "hello";
var str2 = "ehlol";
How can I check whether both strings contain the same characters?
May not be very optimal, but you can simply do
str1.split("").sort().join() == str2.split("").sort().join(); //outputs true
Another suggested approach in one the comments (for optimization in case string length is quite big)
str1.length===str2.length && str1.split("").sort().join() == str2.split("").sort().join(); //first check the length to quickly rule out in case of obvious non-matches
One of the recommended ways to do it is using a hash table: count how many times each character appears. Note that this works best if your characters are ASCII.
The complexity of this algorithm is O(M+N+sigma) where M, N are the lengths of the strings and sigma is the number of distinct letters. The complexity of the accepted solution is higher because of the sorting, which is usually done in O(N*logN), but still a good one if your strings are short. If your strings have hundreds of thousands of characters, then this is the way to go. The drawback of using hash tables is that the memory usage is higher than the solution that uses sorting.
function sameLetters(str1, str2){
var hash = {};
var len1 = str1.length;
var len2 = str2.length;
// Strings with different lengths can't contain the same letters
if(len1 !== len2) return false;
// Count how many times each character appears in str1
for(var i = 0; i < len1; ++i) {
var c = str1[i];
if(typeof hash[c] !== 'undefined') hash[c]++;
else hash[c] = 1;
}
// Make sure each character appearing in str2 was found in str1
for(var i = 0; i < len2; ++i) {
var c = str2[i];
if(typeof hash[c] === 'undefined') return false;
if(hash[c] === 0) return false;
hash[c]--;
}
// Make sure no letters are left
for(var c in hash) {
if(hash[c]) return false;
}
return true;
}
You can then call the function like this (play with it in the browser console):
sameLetters("hello", "ehlol"); // true
sameLetters("hello", "ehllol"); // false
You can use a function for this purpose like sameChars function here-
function myFunction()
{
var input_1 = document.getElementById('input_1').value;
var input_2 = document.getElementById('input_2').value;
var result = sameChars(input_1,input_2);
document.getElementById("demo").innerHTML = result;
}
function sameChars(firstStr, secondStr)
{
var first = firstStr.split('').sort().join('');
var second = secondStr.split('').sort().join('');
return first.localeCompare(second)==0;
}
<input type="text" maxlength="512" id="input_1"/>
<input type="text" maxlength="512" id="input_2"/>
<button onclick="myFunction()">Check If Shuffled</button>
<p id="demo"></p>
Here's a modified version of Gurvinders answer.
var str1 = "hello",
str2 = "ehlol";
// Add sort on prototype of String object
String.prototype.sort = function () {
return this.split('').sort().join('');
};
// First check if length of both is same
var same = str1.length === str2.length && str1.sort() === str2.sort();
console.log('Strings are same?', same);
You could possibly say this:
(a.length === b.length) && (a.split('').every(function(val) { return b.indexOf(val) > -1}))
And, in ES6 you could make it look as follows:
(a.length === b.length) && a.split('').every(val => { return b.indexOf(val) > -1 })

A function that will search an array for letters and return the position in which those letters are

I've created a function that will search for individual letters from a string regardless of case and order. Here is what it looks like.
function match(string, pattern) {
string = string.toLowerCase();
pattern = pattern.toLowerCase();
var patternarray = pattern.split("");
for (i = 0; i < pattern.length; i++) {
if (patternarray[i] >= "a" && patternarray[i] <= "z") {
if (string.indexOf(patternarray[i]) == -1) return false
}
}
return true
}
Now I want to do a similar thing except this time I will be searching an array, and instead of returning true/false I would like to return a new array containing the places in which the string pops up.
For example, if my variable contents were ["Oranges","Apples","Bananas"] and my search was "n", the function would return [0,2]. I am a beginner with JavaScript so thorough explanation would be helpful.
Thanks!
function matchArray(array, pattern) {
var i,
len = array.length,
result = [];
for (i = 0; i < len; ++i) {
if (match(array[i], pattern)) {
result.push(i);
}
}
return result;
}
Underscorejs has a function that should take care of this for you. Just take a look at the filter function. It has the ability to return a new array with items that passed a truth test.
var aArray = _.filter(['asdf','ddd','afsf'], function(item){
return (item.indexOf('a') !== -1);
}

Categories