var isAnagram = function(s, t) {
const len = s.length;
if (len !== t.length) return false;
const hashTab = {};
for (let i = 0; i < len; i++) {
if (!hashTab[s[i]]) {
hashTab[s[i]] = 1;
} else {
hashTab[s[i]]++;
}
if (!hashTab[t[i]]) {
hashTab[t[i]] = -1;
} else {
hashTab[t[i]]--;
}
}
for (let item in hashTab) {
if (hashTab[item]) return false;
}
return true;
Having a hard time figuring out the space complexity of this algorithm. My hypothesis is O(n) as the hashtable grows in size in relations to the input s. This question assumes that the string only contains lowercase letters.
As you mentioned that the string only contains lowercase letters, we can convert characters in given string into corresponding integers ranging from 0 to 25.
To keep track of frequencies of these letters we can declare an array of exactly 26 size space. So, overall space complexity will be O(M), where M is total different letters. In this case, M=26. So, space complexity is O(26).
var isAnagram = function(s, t) {
const len = s.length;
if (len !== t.length) return false;
hashTab = new Array(26);
for(i = 0; i<26; i++){
hashTab[i] = 0;
}
for (let i = 0; i < len; i++) {
idx = s[i].charCodeAt()-97;
hashTab[ idx ]++;
idx = t[i].charCodeAt()-97;
hashTab[ idx ]--;
}
for(i = 0; i<26; i++){
if(hashTab[i]!=0) return false;
}
return true;
}
The major space cost in your code is the hashmap which may contain a frequency counter for each lower case letter from a to z. So in this case, the space complexity is supposed to be constant O(26).
To make it more general, the space complexity of this problem is related to the size of alphabet for your input string. If the input string only contains ASCII characters, then for the worst case you will have O(256) as your space cost. If the alphabet grows to the UNICODE set, then the space complexity will be much worse in the worst scenario.
So in general its O(size of alphabet).
Since the used space is constant (26 letters/items in this case - or even if you used an array of 256 items) and not dependent on the length of the input(s), space complexity is O(1).
Also see this answer.
Related
My code works and I need help figuring out how to optimize it. If possible, don't give me any code, just tips on optimizing it, please.
The rules for the puzzle are:
Each triplet has the rules of how the letters are ordered in the secret word (each letter is followed by the next letter inside the triplet array).
all the letters of the secret word are distinct.
Example input:
triplets1 = [
['t','u','p'],
['w','h','i'],
['t','s','u'],
['a','t','s'],
['h','a','p'],
['t','i','s'],
['w','h','s']
]
Expected Output: "whatisup"
I came up with this code, which works. It selects the first letters that are not preceded by any others in the arrays they are placed, removes them, concatenates them into the final word, and keep doing that until all the arrays are empty. The code isn't being accepted though, because is exceeding the time limit.
function recoverSecret(triplets) {
let secretWord = '', character = '';
let notEmpty = true;
let size = triplets.length;
//it loops until array is empty
while(notEmpty) {
notEmpty = false;
for (let i = 0; i < size; i++) {
for (let j = 0; j < triplets[i].length; j++) {
if (character) j = 0; //everytime a character is included, this condition is truthy, so you have to go back to the start of the array because the character was removed last iteration
character = triplets[i][j];
let remove = []; //this array will have the positions of the letter to remove in the removal cycle
for (let k = 0; k < size; k++) {
if (character == triplets[k][0]) remove.push(k);
//if the letter is in the triplet and it's not the first position, then it isn't the letter we're looking for, so character equals to '', otherwise it will be the letter which will be added to the secretWord string
if (k != i && (triplets[k].includes(character))) {
if (character != triplets[k][0]) {
character = '';
break;
}
}
}
secretWord += character;
if (character) {
//if character is not '', then a removal loop is done to remove the letter because we just found its place
for (x of remove) {
triplets[x].shift();
}
}
// if (triplets[i].length == 0) break;
}
if (triplets[i] != 0) notEmpty = true; //if every triplet is empty, notEmpty remains false and while loop is over
}
}
return secretWord;
}
If possible I don't want any code, just tips on optimization, please.
After some thought, I got it right. The most important change was realizing that I didn't have to loop through each char in the array. I only needed to do it for the first char of each triplet sequence.
Then, made some other adjustments, like, instead of two ifs for checking if the char is the first element of every triplet it's in, I used the condition triplets[k].indexOf(character) > 0 to check both things at the same time, since indexOf method returns -1 if it doesn't find what it is looking for.
I'm not saying it's the best it could be, but now it is a little better in performance than it was
function recoverSecret(triplets) {
let secretWord = '', character = '';
let notEmpty = true;
while (notEmpty) {
notEmpty = false;
for (let i = 0; i < triplets.length; i++) {
if (triplets[i].length == 0) continue;
character = triplets[i][0];
let remove = [];
for (let k = 0; k < triplets.length; k++) {
if (triplets[k].indexOf(character) > 0) {
character = '';
break;
}
else if (triplets[k][0] == character) remove.push(k);
}
secretWord += character;
if (character) {
for (x of remove) {
triplets[x].shift();
}
}
if (triplets[i].length != 0) { notEmpty = true; }
console.log(triplets)
}
return secretWord;
}
}
I have this coding challenge that I can't stop thinking about and I couldn't solve.
You are given 2 strings that represent 2 DNA sequences, for example:
dna1 = "ATGGTTAT"
dna2 = "AGTGTTAT"
The sequences sizes are the same (they can have from 7 to 8 characters) and if they have any matching substrings, you should compare them and return the first largest matching substring's position.
Example:
dna1 = "AAGGTGAT"
dna2 = "AATGTGAT"
The output should be 3.
I tried using for loops to iterate and compare both sequences and return the position, but i can't find a way to separate the matching substrings, compare them and return the position.
Is there any easy way for doing that? Or is there a better language to do this?(I used javascrip)
Since you are only comparing characters in the same position in both strings, you only need one loop:
function mm(a, b) {
let longest = 0;
let longestPos = -1;
let pos = 0;
let len = 0;
while (pos < a.length) {
if (a[pos] == b[pos]) {
len++;
if (len > longest) {
longest = len;
longestPos = pos - len +1;
}
} else {
len = 0;
}
pos++;
}
return longestPos;
}
console.log(mm('AAGGTGAT', 'AATGTGAT'))
examples:
testing("xyzy**") should be true.
testing("xyzy*") should be false.
Reasoning:
In the first case, it is true because one * can behave as a x and the other a z, so all characteres would of the same amount of y.
In the second case there wouldn't be the same amount of characters repeating itself, cause there is only one *, so it is false.
Here is what I have up until now:
const testing = string => {
var array = []; //array with each character without repeating
var array_R = []; //array with the value the value that each character repeats itself
var array_C = 0; //counter for the size of "array"
//getting the character without repeating
for (var i = 0; i < string.length; i++) {
if (!array.includes(string.charAt(i))) {
array[array_C] = string.charAt(i);
array_R[array_C] = 0;
array_C++;
}
}
//how many each character repeats itself
for (var i = 0; i < array.length; i++) {
for (var j = 0; j < string.length; j++) {
if(array[i] == string.charAt(j)){
array_R[i] = array_R[i] + 1;
}
}
}
}
I really don't know how I can proceed from here.
First count up the number of occurrences of each non-* character into an object or Map. Find the maximum value in that collection, then find the sum of all differences between the maximum value and each value. If the number of asterisks is the same as the sum of differences, the repeat conditions are fulfilled.
You'll also have to consider the case where there are more asterisks left over after all holes are filled in to make the values equal - figure out how many are left over, and see if that evenly divides the number of separate characters.
const testing = (str) => {
const grouped = {};
for (const char of str) {
if (char !== '*') grouped[char] = (grouped[char] || 0) + 1;
}
const values = Object.values(grouped);
const numOfSeparateChars = values.length;
const max = Math.max(...values);
const sum = values.reduce((a, b) => a + b, 0);
const sumOfDifferencesFromMax = max * numOfSeparateChars - sum;
const numberAsterisks = (str.match(/\*/g) || []).length;
const remainingAsterisks = sumOfDifferencesFromMax - numberAsterisks;
// Now all characters have been filled in to make the values even
// eg: 'abcc**' -> 'abccab'
// But there may be more asterisks
// eg: 'abcc*****' -> 'abccaabbc'
return remainingAsterisks % numOfSeparateChars === 0;
};
console.log(testing("xyzy**"));
console.log(testing("xyzy*"));
I'm learning to analyze space complexity, but I'm confused of analyzing an array vs an object in JS. So I'd like to get some help here.
ex1. array []
int[] table = new int[26];
for (int i = 0; i < s.length(); i++) {
table[s.charAt(i) - 'a']++;
}
ex1. is from an example online, and it says the space complexity is O(1) because the table's size stays constant.
ex2. object {}
let nums[0,1,2,3,4,5], map = {};
for (let i = 0; i < nums.length; i++) {
map[ nums[i] ] = i;
}
I think ex2. uses O(n) because the map object is accessed 6 times. However, if I use the concept learned from ex1., the space complexity should be O(1)? Any ideas where I went wrong?
From the complexity analysis point of view, in ex 1, the complexity is O(1) because the array size doesn't increase. Because you are initializing the table to a fixed size of 26 (Looks like you are counting the characters in a string?).
See the below example that keeps track of counts of a alphabets in a string (Only small letters for clarity). In this case the length of array which tracks the count of alphabets never change even if the string changes its length.
function getCharacterCount(s){
const array = new Int8Array(26);
for (let i = 0; i < s.length; i++) {
array[s.charCodeAt(i) - 97]++;
}
return array;
}
Now let's change the implementation to map instead. Here the size of the map increases as and when a new character is encountered in the string.So
Theoretically speaking, the space complexity is O(n).
But in reality, we started with map with length 0 (0 keys) and it doesn't go beyond 26. If the string doesn't contain all the characters, the space taken would be much lesser than an array as in previous implementation.
function getCharacterCountMap(s){
const map = {};
for (let i = 0; i < s.length; i++) {
const charCode = s.charCodeAt(i) - 97;
if(map[charCode]){
map[charCode] = map[charCode] + 1
}else{
map[charCode] = 0;
}
}
return map;
}
function getCharacterCount(s){
const array = new Int8Array(26);
for (let i = 0; i < s.length; i++) {
array[s.charCodeAt(i) - 97]++;
}
return array;
}
function getCharacterCountMap(s){
const map = {};
for (let i = 0; i < s.length; i++) {
const charCode = s.charCodeAt(i) - 97;
if(map[charCode]){
map[charCode] = map[charCode] + 1
}else{
map[charCode] = 1;
}
}
return map;
}
console.log(getCharacterCount("abcdefabcedef"));
console.log(getCharacterCountMap("abcdefabcdef"));
In principle this question can be answered language-independent, but specifically I am looking for a Javascript implementation.
Are there any libraries that allow me to measure the "identicality" of two strings? More generally, are there any algorithms that do this, that I could implement (in Javascript)?
Take, as an example, the following string
Abnormal Elasticity of Single-Crystal Magnesiosiderite across the Spin
Transition in Earth’s Lower Mantle
And also consider the following, slightly adjusted string. Note the boldface parts that are different
bnormal Elasticity of Single Crystal Magnesio-Siderite across the Spin-Transition in Earths Lower Mantle.
Javascript's native equality operators won't tell you a lot about the relation between these strings. In this particular case, you could match the strings using regex, but in general that only works when you know which differences to expect. If the input strings are random, the generality of this approach breaks down quickly.
Approach... I can imagine writing an algorithm that splits up the input string in an arbitrary amount N of substrings, and then matching the target string with all those substrings, and using the amount of matches as a measurement of identicality. But this feels like an unattractive approach, and I wouldn't even want to think about how big O will depend on N.
It would seem to me that there are a lot of free parameters in such an algorithm. For example, whether case-sensitivity of characters should contribute equally/more/less to the measurement than order-preservation of characters, seems like an arbitrary choice to make by the designer, i.e.:
identicality("Abxy", "bAxy") versus identicality("Abxy", "aBxy")
Defining the requirements more specifically...
The first example is the scenario in which I could use it. I'm loading a bunch of strings (titles of academic papers), and I check whether I have them in my database. However, the source might contain typos, differences in conventions, errors, whatever, which makes matching hard. There probably is a more easy way to match titles in this specific scenario: as you can sort of expect what might go wrong, this allows you to write down some regex beast.
You can implement Hirschberg's algorithm and distinguish delete/insert operations (or alter Levenshtein).
For Hirschbers("Abxy", "bAxy") the results are:
It was 2 edit operations:
keep: 3
insert: 1
delete: 1
and for Hirschbers("Abxy", "aBxy") the results are:
It was 2 edit operations:
keep: 2
replace: 2
You can check the javascript implementation on this page.
'Optimal' String-Alignment Distance
function optimalStringAlignmentDistance(s, t) {
// Determine the "optimal" string-alignment distance between s and t
if (!s || !t) {
return 99;
}
var m = s.length;
var n = t.length;
/* For all i and j, d[i][j] holds the string-alignment distance
* between the first i characters of s and the first j characters of t.
* Note that the array has (m+1)x(n+1) values.
*/
var d = new Array();
for (var i = 0; i <= m; i++) {
d[i] = new Array();
d[i][0] = i;
}
for (var j = 0; j <= n; j++) {
d[0][j] = j;
}
// Determine substring distances
var cost = 0;
for (var j = 1; j <= n; j++) {
for (var i = 1; i <= m; i++) {
cost = (s.charAt(i-1) == t.charAt(j-1)) ? 0 : 1; // Subtract one to start at strings' index zero instead of index one
d[i][j] = Math.min(d[i][j-1] + 1, // insertion
Math.min(d[i-1][j] + 1, // deletion
d[i-1][j-1] + cost)); // substitution
if(i > 1 && j > 1 && s.charAt(i-1) == t.charAt(j-2) && s.charAt(i-2) == t.charAt(j-1)) {
d[i][j] = Math.min(d[i][j], d[i-2][j-2] + cost); // transposition
}
}
}
// Return the strings' distance
return d[m][n];
}
alert(optimalStringAlignmentDistance("Abxy", "bAxy"))
alert(optimalStringAlignmentDistance("Abxy", "aBxy"))
Damerau-Levenshtein Distance
function damerauLevenshteinDistance(s, t) {
// Determine the Damerau-Levenshtein distance between s and t
if (!s || !t) {
return 99;
}
var m = s.length;
var n = t.length;
var charDictionary = new Object();
/* For all i and j, d[i][j] holds the Damerau-Levenshtein distance
* between the first i characters of s and the first j characters of t.
* Note that the array has (m+1)x(n+1) values.
*/
var d = new Array();
for (var i = 0; i <= m; i++) {
d[i] = new Array();
d[i][0] = i;
}
for (var j = 0; j <= n; j++) {
d[0][j] = j;
}
// Populate a dictionary with the alphabet of the two strings
for (var i = 0; i < m; i++) {
charDictionary[s.charAt(i)] = 0;
}
for (var j = 0; j < n; j++) {
charDictionary[t.charAt(j)] = 0;
}
// Determine substring distances
for (var i = 1; i <= m; i++) {
var db = 0;
for (var j = 1; j <= n; j++) {
var i1 = charDictionary[t.charAt(j-1)];
var j1 = db;
var cost = 0;
if (s.charAt(i-1) == t.charAt(j-1)) { // Subtract one to start at strings' index zero instead of index one
db = j;
} else {
cost = 1;
}
d[i][j] = Math.min(d[i][j-1] + 1, // insertion
Math.min(d[i-1][j] + 1, // deletion
d[i-1][j-1] + cost)); // substitution
if(i1 > 0 && j1 > 0) {
d[i][j] = Math.min(d[i][j], d[i1-1][j1-1] + (i-i1-1) + (j-j1-1) + 1); //transposition
}
}
charDictionary[s.charAt(i-1)] = i;
}
// Return the strings' distance
return d[m][n];
}
alert(damerauLevenshteinDistance("Abxy", "aBxy"))
alert(damerauLevenshteinDistance("Abxy", "bAxy"))
Optimal String Alignment has better performance
Optimal String Alignment Distance 0.20-0.30ms
Damerau-Levenshtein Distance 0.40-0.50ms