I'm trying to list all three letter permutations and this is the code I have -
window.permute = function(){
var alphabet = "abcdefghijklmnopqrstuvwxyz";
var searchTerm ="aaa";
var position = 2;
changeString(searchTerm, position);
}
window.changeString = function(searchTerm, position){
if (position <0){
alert(newString);
return;
}
var alphabet = "abcdefghijklmnopqrstuvwxyz"
for (j=0; j < 26;j++){
var newString = searchTerm.substr(0, position) + alphabet[j] + searchTerm.substr(position+1);
var newPosition = position -1;
changeString(newString,newPosition);
}
return;
}
It's not working and I'm not sure why- can anyone help?
var permutate = (function() {
var results = [];
function doPermute(input, output, used, size, level) {
if (size == level) {
var word = output.join('');
results.push(word);
return;
}
level++;
for (var i = 0; i < input.length; i++) {
if (used[i]) {
continue;
}
used[i] = true;
output.push(input[i]);
doPermute(input, output, used, size, level);
used[i] = false;
output.pop();
}
}
return {
getPermutations: function(input, size) {
var chars = input.split('');
var output = [];
var used = new Array(chars.length);
doPermute(chars, output, used, size, 0);
return results;
}
}
})();
for more information, visit http://jinwolf.tumblr.com/post/26476479113/draw-something-cheat
for an working example, check this jsfiddle http://jsfiddle.net/jinwolf/Ek4N5/31/
alert(newString);
newString is not defined right there. Instead, you should use the argument passed:
alert(searchTerm);
Edit: I'm not entirely sure of your approach. It seems overly complicated. This seems to work. I understand that you rather have your own code working, but perhaps this helps you in solving. I don't quite get your substr part.
http://jsfiddle.net/NUG2A/2/
var alphabet = "abc"; // shortened to save time
function permute(text) {
if(text.length === 3) { // if length is 3, combination is valid; alert
console.log(text); // or alert
} else {
var newalphabet = alphabet.split("").filter(function(v) {
return text.indexOf(v) === -1;
}); // construct a new alphabet of characters that are not used yet
// because each letter may only occur once in each combination
for(var i = 0; i < newalphabet.length; i++) {
permute(text + newalphabet[i]); // call permute with current text + new
// letter from filtered alphabet
}
}
}
permute("");
This will result in the following being called:
permute("");
permute("a");
permute("ab");
permute("abc"); // alert
permute("ac");
permute("acb"); // alert
permute("b");
// ...
I'm not sure from your question that you mean "permutations" because usually permutations do not include repeated elements where it looks like you want to include "aaa".
Here are several algorithms for listing permutations you can go check out. If it turns out you mean to have repetitions, it looks like pimvdb has you covered.
Edit: So you know what you are getting into run-time wise:
With repetition (aaa,aab,...): n^k = 26^3 = 17,576
Without repetition (abc,bac,...): n!/(n-k)! = 26!/(26-3)! = 15,600
for (j=0; j < 26;j++){
should be
for (var j=0; j<26; j++) {
Without the declaration, j is a global variable, so it only takes one iteration to get to 26 and then all the loops terminate.
For permutations a recursive algorith as pimvd showed is always nice but don't forget you can just brute force it with for-loops when N is small:
for(int x1=0; x1 < 26; x1++)
for(int x2=0; x2 < 26; x2++)
for(int x3=0; x3 < 26; x3++){
//do something with x1, x2, x3
}
In C#:
void DoPermuation(string s)
{
var pool = new HashSet<string>();
//Permute("", , pool);
pool = Permute(new List<char>(s));
int i = 0;
foreach (var item in pool) Console.WriteLine("{0:D2}: {1}", ++i, item);
}
HashSet<string> Permute(List<char> range)
{
if (range.Count == 1) return new HashSet<string>(new string[] { range[0].ToString() });
var pool = new HashSet<string>();
foreach (var c in range)
{
var list = new List<char>(range);
list.Remove(c);
foreach (var item in Permute(list)) pool.Add(c + item);
}
return pool;
}
Related
I am making a javascript function that will input a string, and output a "spongebob mocking text"
basically, you input "Hello, this is a message to the world" and you would get "HeLlO, ThIS iS a MeSsAGe tO tHE wORlD"
basically, randomly decide wheather to capitalize a letter or not. I made a function which i thought would do that, but it didn't work. here is the code that I tested in the js console:
function memify(input) { // function called memify()
var il = input.length; // gets the length of the input
var newinput = input; // creates a new variable that will be changed from input.
for (var i=0;i>il;i++) {
var rng = Math.floor((Math.random()*2)); // random number between 0 and 1. 0 = upper 1 = lower
if (rng === 0) {
newinput.charAt(i).toUpperCase();
}
else {
newinput.charAt(i).toLowerCase();
}
}
return newinput;
}
var text = prompt();
var textmeme = memify(text);
alert(textmeme);
Why is this not working? Do I have an error in my code? Any help will be greatly appreciated.
When you do
newinput.charAt(i).toUpperCase();
you're creating a new uppercase character, but you aren't doing anything with it; it's just an unused expression, so there's no visible change. Primitives (including strings) are immutable - you should explicitly reassign a string to something else (eg newString += newinput.charAt(i).toUpperCase();) to see an effect.
You also need to use
for (var i = 0; i < il; i++) {
// ^
instead of
for (var i = 0; i > il; i++) {
// ^
else, no iterations will run at all.
function memify(input) { // function called memify()
var il = input.length; // gets the length of the input
let changedStr = '';
for (var i = 0; i < il; i++) {
var rng = Math.floor((Math.random() * 2)); // random number between 0 and 1. 0 = upper 1 = lower
if (rng === 0) {
changedStr += input.charAt(i).toUpperCase();
} else {
changedStr += input.charAt(i).toLowerCase();
}
}
return changedStr;
}
var text = prompt();
var textmeme = memify(text);
console.log(textmeme);
Another option, using .map, which looks much cleaner IMO:
const memify = input => [...input]
.map(char => Math.random() < 0.5 ? char.toUpperCase() : char.toLowerCase())
.join('');
console.log(memify(prompt()));
Or more concise, safer and generally better solution :). It does not require for loop, checking length of string and other error prone stuff.
function memify(input) {
var rng = () => Math.random() > 0.5;
var res = input.split('').map( letter =>
rng() ? letter.toUpperCase() : letter.toLowerCase()
).join('');
return res;
}
var textmeme = memify("Hello World");
console.log(textmeme);
Please up-vote if it was helpful :)
Alice has two strings, initial and goal. She can remove some number of characters from initial, which will give her a subsequence of that string. A string with no deletions is still considered a subsequence of itself. Given these two strings, can you find the minimum number of subsequences of initial that, when appended together, will form goal?
Functions
minimumConcat() has two parameters:
initial: the source string that you will get subsequences from
goal: the target string that needs to be formed
Input Format
For some of our templates, we have handled parsing for you. If we do not provide you a parsing function, you will need to parse the input directly. In this problem, our input format is as follows:
The first line is the initial String that we will be generating subsequences from
The second line is the goal String to form
Here is an example of the raw input:
abc
bcbac
Expected Output
Return the number of minimum possible subsequences of initial that can be appended together to form goal.
If there are no possible solutions, return -1.
Example minimumConcat() Input #1
initial: "xyz"
goal: "xzyxz"
Output: 3
function minimumConcat(initial, goal) {
//Put your code here.
return 0;
}
Loop the initial string array to form the goal string array.
function minimumConcat(initial, goal) {
initial = initial.split('');
goal = goal.split('');
let res,count=0;
while(true){
if(goal.length > 0){
res = checkChar(initial,goal);
if(false === res){
return -1;
}
}else{
return count;
}
goal = res;
count++;
}
}
function checkChar(initial,goal){
let started = false;
let foundIndex = 0;
for(let i=0; i<initial.length; i++){
if(initial[i] == goal[foundIndex]){
started = true;
foundIndex++;
}
}
if(started){
return goal.slice(foundIndex);
}else{
return false;
}
}
console.log(minimumConcat('abc','bcbac'));
Here you go!
function minimumConcat(initial, goal) {
let result = 0;
let pattern = '';
let count1 = Array.apply(null, Array(26)).map(Number.prototype.valueOf, 0);
let count2 = Array.apply(null, Array(26)).map(Number.prototype.valueOf, 0);
initial.split('').forEach(c => {
pattern = pattern + c
});
pattern = "^[" + pattern + "]*$";
if (!RegExp(pattern).test(goal)) return -1;
for (let i = 0; i < initial.length; i++) {
count1[initial.charCodeAt(i) - 97]++;
}
for (let i = 0; i < goal.length; i++) {
count2[goal.charCodeAt(i) - 97]++;
}
for (let i = 0; i < 26; i++) {
result += Math.abs(count1[i] - count2[i]);
}
return result;
}
console.log(minimumConcat("abc", "bcbac"));
Since this looks like homework I won't give the solution right away, instead here is a suggestion on how to solve it:
I think the hardest part is finding all the sub-strings if you are using Python that's simplified by itertools as mentioned here.
Edit, I didn't notice the javascript tag, you can get the substring set, without a library, with a couple of for loops.
After having all combinations from initial you can sort them to have the longest first. And then go one by one removing them from goal. Counting every time you remove. If, after iterating over all sub-strings, goal is not an empty string then no subsequence of initial can construct goal.
This answers your question using Java
public static int minimumConcat(String initial, String goal) {
HashSet<Character> set = new HashSet<>();
for(char c : initial.toCharArray()) set.add(c);
for(char c : goal.toCharArray()) {
if(!set.contains(c)) return -1;
}
int j = 0, result = 0;
for(int i = 0; i < goal.length();) {
char c = goal.charAt(i);
while(j < initial.length() && initial.charAt(j) != c) j++;
if(j == initial.length()) {
j = 0;
result++;
} else {
j++;
i++;
}
}
result++;
return result;
}
Here is what I've done with python
def minimumConcat(initial, goal):
#verify that goal has all character of initial
res = 0
for i in goal:
if i in initial:
pass
else:
res=-1;
if res != -1:
while goal!="":
a = removefirstGreatestSubstring(initial,goal)
goal=a["goal"];
if a["has"] ==True :
res=res+1
#find the greatest concat
print(res)
def removefirstGreatestSubstring(initial,goal):
has_subtring = False
start = 0
for car in initial:
if car == goal[start]:
has_subtring= True
start = start+1
finalgoal=goal[start:]
return {"goal": finalgoal, "has":has_subtring}
initial = "abc"
goal = "bcbac"
b = minimumConcat(initial, goal)
I've made it using a different approach with regular expressions.
Here a clean version of the code:
"use strict";
// Solution:
function minimumConcat(initial, goal) {
let result = -1;
let goal_slice = goal;
let exp = "", sub = "";
let initial_concat = "";
let matches = 0, count = 0;
let initial_arr = initial.split('');
for(let i = 0 ; i<initial_arr.length; i++){
for(let j = initial_arr.length ; j>i+1; j--){
sub = initial.slice(i,j);
exp = new RegExp(sub,"ig");
matches = (goal_slice.match(exp) || []).length;
if(matches>=1) {
count +=matches;
initial_concat+=sub.repeat(matches);
goal_slice = goal_slice.slice((goal_slice.lastIndexOf(sub)+sub.length));
}
}
}
result = (initial_concat==goal)? count : result;
return result;
}
// Test cases:
let test_cases = [
{initial:"abc", goal: "abcbc"}, // expected result 2
{initial:"abc", goal: "acdbc"}, // expected result -1
{initial:"bcx", goal: "bcbcbc"}, // expected result 3
{initial:"xyz", goal: "xyyz"}, // expected result 2
]
// Running the tests:
test_cases.forEach(function(item,index){
console.log(minimumConcat(item.initial, item.goal));
});
Also, I've included a debug flag to turn on/off console.log messages in order to anybody could easily understand what is happening on each iteration cycle.
"use strict";
// Shwitch for debugging:
const debug = true;
// Solution:
function minimumConcat(initial, goal) {
let exp = "";
let sub = "";
let match = 0;
let count = 0;
let result = -1;
let goal_slice = goal;
let initial_concat = "";
let initial_arr = initial.split('');
for(let i = 0 ; i<initial_arr.length; i++){
for(let j = initial_arr.length ; j>i+1; j--){
sub = initial.slice(i,j);
exp = new RegExp(sub,"ig");
match = (goal_slice.match(exp) || []).length;
if(match>=1) {
count +=match;
initial_concat+=sub.repeat(match);
goal_slice = goal_slice.slice((goal_slice.lastIndexOf(sub)+sub.length));
}
if(debug){
console.log("-----------------------------");
console.log(" i:", i, " - j:", j);
console.log(" exp:", exp);
console.log(" goal:", goal);
console.log(" goal_slice:", goal_slice);
console.log(" match:",match);
}
}
}
result = (initial_concat==goal)? count : result;
if(debug){
console.log("---RESULTS:--------------------------");
console.log("count:",count);
console.log("goal vs initial_concat: ", goal, " - ", initial_concat);
console.log("result: ", result);
}
return result;
}
// Test cases:
let test_cases = [
{initial:"abc", goal: "abcbc"}, // expected result 2
{initial:"abc", goal: "acdbc"}, // expected result -1
{initial:"bcx", goal: "bcbcbc"}, // expected result 3
{initial:"xyz", goal: "xyyz"}, // expected result 2
]
// Running the tests:
test_cases.forEach(function(item,index){
if(debug){
console.log("-----------------------------");
console.log("TEST CASE #",index,":");
console.table(item);
}
minimumConcat(item.initial, item.goal);
});
here is in php
public function index()
{
$init="abc";
$goal="abacabacabacacb";
$res=$this->minimum($init,$goal);
}
public function check($inital,$goal){
$inital=str_split( $inital);
$goal=str_split( $goal);
for($i=0;$i<sizeof($goal);$i++){
if(!in_array($goal[$i],$inital)){
return -1;
}
}
return 0;
}
public function minimum($inital,$goal){
$res=$this->check($inital,$goal);
if($res==-1){
return -1;
}
$counter=0;
$c=0;
$inital=str_split( $inital);
$goal=str_split( $goal);
for($i=0;$i<sizeof($goal);$i++){
for($j=0;$j<sizeof($inital);$j++){
if(($i+$c)<sizeof($goal)){
echo " ".$i." > ".$j." > ".$c." /// ";
if($goal[$i+$c]==$inital[$j]){
$c+=1;
}
}
}
$counter+=1;
if(($i+$c)>=sizeof($goal)){
break;
}
$c=$c-1;
}
return $counter;
}
Here is my python solution
def check_char(initial, goal):
N = len(initial)
started = False
found_index = 0
for i in range(N):
if (initial[i] == goal[found_index]):
started = True
found_index += 1
if(started):
return goal[found_index:]
else:
return '-'
def minimumConcat(initial, goal):
res = 0
count = 0
while(True):
if( len(goal) > 0 ):
res = check_char(initial, goal)
if(res == '-'):
print(-1)
break;
else:
print(count)
break;
goal = res
count += 1
initial = input()
goal = input()
minimumConcat(initial, goal)
I have encountered a very strange bug:
I derive a new array allSavings[] from another one (tours[]) and sort it in the function calculateAllSavings(). Before I call the function I can access tours[] just fine, but afterwards, I can't anymore. The div tags demo1 and demo2 both exist and are working fine for other outputs.
function euclDist(node1,node2){
if(node1 != node2){
var x = Math.pow(nodes[node2].x - nodes[node1].x,2);
var y = Math.pow(nodes[node2].y - nodes[node1].y,2);
var dist = Math.sqrt(x+y);
return dist;
}
else return 0.0;
}
function tourDist(members){
var tourDist = 0.0;
if (members.length>1){
for (i = 1; i < members.length; i++)
tourDist += euclDist(members[i],members[i-1]);
}
return tourDist;
}
function combineTours(tourA, tourB){
tourA.pop();
tourB.shift();
return tourA.concat(tourB);
}
function calculateSaving(tourA,tourB){
var costSeparate = tourDist(tourA) + tourDist(tourB);
var combTour = combineTours(tourA,tourB);
var costCombined = tourDist(combTour);
return costSeparate - costCombined;
}
function calculateAllSavings(){
var allPossibilities = [];
for(var i = 0; i < tours.length; i++){
for(var j = 0; j < tours.length; j++){
if(i != j)
var savingObj = {saving:calculateSaving(tours[i],tours[j]), tourA: i, tourB: j};
allPossibilities.push(savingObj);
}
}
allPossibilities.sort(function(a, b){
return b.saving-a.saving
})
document.getElementById("demo3").innerHTML = "success";
return allPossibilities;
}
//Initialize Array
var tours = [];
tours.push([0,1,2,3,0]);
tours.push([0,4,5,6,0]);
tours.push([0,7,8,0]);
tours.push([0,9,10,0]);
//BUG
document.getElementById("demo1").innerHTML = tours.join('\n'); // Shows array correctly
var allSavings = calculateAllSavings(); //BUG APPEARS HERE
document.getElementById("demo2").innerHTML = tours.join('\n'); // Doesn't show anything
Edit Solved:
combine() was overwriting the original tours[].
by doing the combining with cloned tours, the original was left untouchted.
function combineTours(tourA, tourB){
var tour1 = tourA.slice(0);
var tour2 = tourB.slice(0);
tour1.pop();
tour2.shift();
return tour1.concat(tour2);
}
Thanks to everyone who helped me
Well, in combineTours function you're calling .pop() method on one array and .shift() method on another, which removes one element from each of these arrays. In calculateAllSavings you're calling calculateSaving in a loop and it's calling combineTours, so you're effectively removing all elements from the sub-arrays.
Maybe you should just remove these lines from combineTours:
tourA.pop();
tourB.shift();
For the future: use console.log() for debugging, it could help you identify the issue.
Can you try this?
for(var i = 0; i < tours.length; i++){
for(var j = 0; j < tours[i].length; j++){
if(i != j)
var savingObj = {saving:calculateSaving(tours[i],tours[j]), tourA: i, tourB: j};
allPossibilities.push(savingObj);
}
}
Apart from this, you can also debug and see if your document.getElementById("demo2").innerHTML = tours.join('\n'); line actually gets executed. You may be running an infinite loop. Try and debug your code using chrome developer tools.
Say you have the following string:
FJKAUNOJDCUTCRHBYDLXKEODVBWTYPTSHASQQFCPRMLDXIJMYPVOHBDUGSMBLMVUMMZYHULSUIZIMZTICQORLNTOVKVAMQTKHVRIFMNTSLYGHEHFAHWWATLYAPEXTHEPKJUGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNT
LDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFY
FFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQ
XBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGR
AMELUTEPYILBIUOCKKUUBJROQFTXMZRLXBAMHSDTEKRRIKZUFNLGTQAEUINMBPYTWXULQNIIRXHHGQDPENXAJNWXULFBNKBRINUMTRBFWBYVNKNKDFR
I'm trying to find the smallest substring containing the letters ABCDA.
I tried a regex approach.
console.log(str.match(/[A].*?[B].*?[C].*?[D].*?[A]/gm).sort((a, b) => a.length - b.length)[0]);
This works, but it only find strings where ABCDA appear (in that order). Meaning it won't find substring where the letters appear in a order like this: BCDAA
I'm trying to change my regex to account for this. How would I do that without using | and type out all the different cases?
You can't.
Let's consider a special case: Assume the letters you are looking for are A, A, and B. At some point in your regexp there will certainly be a B. However, the parts to the left and to the right of the B are independent of each other, so you cannot refer from one to the other. How many As are matched in the subexpression to the right of the B depends on the number of As being already matched in the left part. This is not possible with regular expressions, so you will have to unfold all the different orders, which can be many!
Another popular example that illustrates the problem is to match opening brackets with closing brackets. It's not possible to write a regular expression asserting that in a given string a sequence of opening brackets is followed by a sequence of closing brackets of the same length. The reason for this is that to count the brackets you would need a stack machine in contrast to a finite state machine but regular expressions are limited to patterns that can be matched using FSMs.
This algorithm doesn't use a regex, but found both solutions as well.
var haystack = 'FJKAUNOJDCUTCRHBYDLXKEODVBWTYPTSHASQQFCPRMLDXIJMYPVOHBDUGSMBLMVUMMZYHULSUIZIMZTICQORLNTOVKVAMQTKHVRIFMNTSLYGHEHFAHWWATLYAPEXTHEPKJUGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNTLDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFYFFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQXBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGRAMELUTEPYILBIUOCKKUUBJROQFTXMZRLXBAMHSDTEKRRIKZUFNLGTQAEUINMBPYTWXULQNIIRXHHGQDPENXAJNWXULFBNKBRINUMTRBFWBYVNKNKDFR';
var needle = 'ABCDA'; // the order of letters doesn't matter
var letters = {};
needle.split('').forEach(function(ch) {
letters[ch] = letters[ch] || 0;
letters[ch]++;
});
var shortestSubstringLength = haystack.length;
var shortestSubstrings = []; // storage for found substrings
var startingPos = 0;
var length;
var currentPos;
var notFound;
var letterKeys = Object.keys(letters); // unique leters
do {
lettersLeft = JSON.parse(JSON.stringify(letters)); // copy letters count object
notFound = false;
posStart = haystack.length;
posEnd = 0;
letterKeys.forEach(function(ch) {
currentPos = startingPos;
while (!notFound && lettersLeft[ch] > 0) {
currentPos = haystack.indexOf(ch, currentPos);
if (currentPos >= 0) {
lettersLeft[ch]--;
posStart = Math.min(currentPos, posStart);
posEnd = Math.max(currentPos, posEnd);
currentPos++;
} else {
notFound = true;
}
}
});
if (!notFound) {
length = posEnd - posStart + 1;
startingPos = posStart + 1; // starting position for next iteration
}
if (!notFound && length === shortestSubstringLength) {
shortestSubstrings.push(haystack.substr(posStart, length));
}
if (!notFound && length < shortestSubstringLength) {
shortestSubstrings = [haystack.substr(posStart, length)];
shortestSubstringLength = length;
}
} while (!notFound);
console.log(shortestSubstrings);
Maybe not as clear as using regex could be (well, for me regex are never really clear :D ) you can use brute force (not so brute)
Create an index of "valid" points of your string (those with the letters you want) and iterate with a double loop over it getting substrings containing at least 5 of those points, checking that they are valid solutions. Maybe not the most efficient way, but easy to implement, to understand, and probably to optimize.
var haystack="UGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNTLDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFYFFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQXBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGR";
var needle="ABCD";
var size=haystack.length;
var candidate_substring="";
var minimal_length=size;
var solutions=new Array();
var points=Array();
for(var i=0;i<size;i++){
if(needle.indexOf(haystack[i])>-1) points.push(i);
}
var limit_i= points.length-4;
var limit_k= points.length;
for (var i=0;i<limit_i;i++){
for(var k=i;k<limit_k;k++){
if(points[k]-points[i]+1<=minimal_length){
candidate_substring=haystack.substr(points[i],points[k]-points[i]+1);
if(is_valid(candidate_substring)){
solutions.push(candidate_substring);
if(candidate_substring.length < minimal_length) minimal_length=candidate_substring.length;
}
}
}
}
document.write('<p>Solution length:'+minimal_length+'<p>');
for(var i=0;i<solutions.length;i++){
if(solutions[i].length<=minimal_length) document.write('<p>Solution:'+solutions[i]+'<p>');
}
function is_valid(candidate_substring){
//verify we've got all characters
for(var j=0;j<candidate_substring.length;j++){
if(candidate_substring.indexOf(needle.charAt(j))<0) return false;
}
//...and verify we have two "A"
if(candidate_substring.indexOf("A")==candidate_substring.lastIndexOf("A")) return false;
return true;
}
Just had this problem in an interview as a coding assignment and came up with another solution, (it's not as optimal as the one above but maybe it's easier to understand).
function MinWindowSubstring(strArr) {
const N = strArr[0];
const K = strArr[1];
const letters = {};
K.split('').forEach( (character) => {
letters[character] = letters[character] ? letters[character] + 1 : 1;
});
let possibleSequencesList = [];
const letterKeys = Object.keys(letters);
for(let i=0; i< N.length; i++) {
const char = N[i];
if (new String(letterKeys).indexOf(char) !== -1) {
// found a character in the string
// update all previus sequences
possibleSequencesList.forEach((seq) => {
if(!seq.sequenceComplete) {
seq[char] = seq[char]-1;
seq.lastIndex = i;
// check if sequence is complete
var sequenceComplete = true;
letterKeys.forEach( (letter) => {
if(seq[letter] > 0) {
sequenceComplete = false;
}
});
seq.sequenceComplete = sequenceComplete
}
})
// create a new sequence starting from it
const newSeq = {
startPoint: i,
lastIndex: i,
sequenceComplete: false,
...letters
}
newSeq[char] = newSeq[char]-1;
possibleSequencesList.push(newSeq);
}
}
// cleanup sequences
let sequencesList = possibleSequencesList.filter(sequence => sequence.sequenceComplete);
let output = [];
let minLength = N.length;
// find the smalles one
sequencesList.forEach( seq => {
if( (seq.lastIndex - seq.startPoint) < minLength) {
minLength = seq.lastIndex - seq.startPoint;
output = N.substring(seq.startPoint, seq.lastIndex + 1);
}
})
return output;
}
I have a string that looks something like the following 'test:1;hello:five;just:23'. With this string I need to be able to do the following.
....
var test = MergeTokens('test:1;hello:five;just:23', 'yes:23;test:567');
...
The end result should be 'test:567;hello:five;just:23;yes:23' (note the exact order of the tokens is not that important).
Just wondering if anyone has any smart ideas of how to go about this. I was thinking a regex replace on each of the tokens on right and if a replace didn't occur because there was not match just append it. But maybe there is better way.
Cheers
Anthony
Edit: The right side should override the left. The left being what was originally there and the right side being the new content. Another way of looking at it, is that you only keep the tokens on the left if they don't exist on the right and you keep all the tokens on the right.
#Ferdinand
Thanks for the reply. The problem is the efficiency with which the solution you proposed. I was initially thinking down similar lines but discounted it due to the O(n*z) complexity of the merge (where n and z is the number tokens on the left and right respectively) let alone the splitting and joining.
Hence why I was trying to look down the path of a regex. Maybe behind the scenes, regex is just as bad or worse, but having a regex which removes any token from the left string that exists on the right (O(n) for the total amount of token on the right) and then just add the 2 string together (i.e. vat test = test1 + test2) seems more efficient. thanks
I would use join() and split() to create some utility functions to pack and unpack your token data to an object:
// Unpacks a token string into an object.
function splitTokens(str) {
var data = {}, pairs = str.split(';');
for (var i = 0; i < pairs.length; ++i) {
var pair = pairs[i].split(':');
data[pair[0]] = pair[1];
}
return data;
}
// Packs an object into a token string.
function joinTokens(data) {
var pairs = [];
for (var key in data) {
pairs.push(key + ":" + data[key]);
}
return pairs.join(';');
}
Using these, merging is easy:
// Merges all token strings (supports a variable number of arguments).
function mergeTokens() {
var data = {};
for (var i = 0; i < arguments.length; ++i) {
var d = splitTokens(arguments[i]);
for (var key in d) {
data[key] = d[key];
}
}
return joinTokens(data);
}
The utility functions are also useful if you want to extract some keys (say,"test") and/or check for existence:
var data = splitTokens(str);
if (data["test"] === undefined) {
// Does not exist
} else {
alert("Value of 'test': " + data["test"]);
}
The following is what I ended thiking about. What do you guys recon?
Thanks
Anthony
function Tokenizer(input, tokenSpacer, tokenValueSpacer) {
this.Tokenizer = {};
this.TokenSpacer = tokenSpacer;
this.TokenValueSpacer = tokenValueSpacer;
if (input) {
var TokenizerParts = input.split(this.TokenSpacer);
var i, nv;
for (i = 0; i < TokenizerParts.length; i++) {
nv = TokenizerParts[i].split(this.TokenValueSpacer);
this.Tokenizer[nv[0]] = nv[1];
}
}
}
Tokenizer.prototype.add = function(name, value) {
if (arguments.length == 1 && arguments[0].constructor == Object) {
this.addMany(arguments[0]);
return;
}
this.Tokenizer[name] = value;
}
Tokenizer.prototype.addMany = function(newValues) {
for (nv in newValues) {
this.Tokenizer[nv] = newValues[nv];
}
}
Tokenizer.prototype.remove = function(name) {
if (arguments.length == 1 && arguments[0].constructor == Array) {
this.removeMany(arguments[0]);
return;
}
delete this.Tokenizer[name];
}
Tokenizer.prototype.removeMany = function(deleteNames) {
var i;
for (i = 0; i < deleteNames.length; i++) {
delete this.Tokenizer[deleteNames[i]];
}
}
Tokenizer.prototype.MergeTokenizers = function(newTokenizer) {
this.addMany(newTokenizer.Tokenizer);
}
Tokenizer.prototype.getTokenString = function() {
var nv, q = [];
for (nv in this.Tokenizer) {
q[q.length] = nv + this.TokenValueSpacer + this.Tokenizer[nv];
}
return q.join(this.TokenSpacer);
}
Tokenizer.prototype.toString = Tokenizer.prototype.getTokenString;
i am a few years late, but i think this is what you are looking for:
function MergeTokens(input, replace){
var replaceTokens = replace.split(";");
for(i=0; i<replaceTokens.length; i++){
var pair = replaceTokens[i].split(":");
var result = input;
regString = "\\b" + pair[0] + ":[\\w]*\\b";
var reg = new RegExp(regString);
if(reg.test(result)){
result = result.replace(reg, replaceTokens[i]);
}
else{
result = result + replaceTokens[i];
}
}
return result;
}