Javascript Codewars Challenge(isMerge)

时间:2016-08-21 15:30:45

标签: javascript algorithm

我之前已经问过这个问题(the first link to my question),但无法获得问题的完整解决方案。 编写一个算法来检查给定的字符串s是否可以由另外两个字符串part1和part2组成。

限制是part1和part2中的字符与s中的字符顺序相同。

示例:

' codewars'是一个合并来自' cdw'并且' oears':

               c o d e w a r s   = codewars
                part1:  c   d   w         = cdw
                part2:    o   e   a r s   = oears

无论我如何挑战我的解决方案,我都不会轻易通过所有检查。我得到相同的结果。请帮助你做正确的事。谢谢你

                 function isMerge(s, part1, part2) {
                 if(part1.length + part2.length != s.length)
                     {
                       return false;
                      }

                 if (s.length == '')
                      {
                 return true;
                      }
                  for(var i = 0, len = s.length; i < len; i++)
                      {
                       for (var j=0, jen = part1.length; j<jen; j++)
                        {
                           if(s[0] === part1[0])
                           {
                          return true;
                           }
                        }
                   for(var p = 0, pen = part2.length; p < pen; p++)
                   {
                      if(s[0] === part2[0]) 
                      {
                        return true;
                       }

                   }

               }


                 return false;

              }

some checks been passed

checks not being passed

enter image description here

4 个答案:

答案 0 :(得分:3)

代码审核

function isMerge(s, part1, part2) {
  // length comparison is not required
  // this is a roundabout way of saying A === B
  // more comments on this below
  if(part1.length + part2.length != s.length)
  {
    return false;
  }
  if (s.length == '')
  {
    return true;
  }
  for(var i = 0, len = s.length; i < len; i++)
  {
    // you don't have to loop over part1 or part2
    for (var j=0, jen = part1.length; j<jen; j++)
    {
      // s[0] and part1[0] will always be the same char
      // i'm certain you intended s[i] and part1[j]
      if(s[0i] === part1[0j])
      {
        // if you `return` here, you will return lots of false positives
        // the condition above only checks for two matching letters,
        // you have to finish looping thru `s` to know we checked all letters
        return true;
      }
    }
    // this loop is broken the same way as the one above
    for(var p = 0, pen = part2.length; p <l pen; p++)
    {
      if(s[0] === part2[0])
      {
        return true;
      }
    }
  }
  // this is also wrong too.
  // what we will do is, if at anytime there is a non-match in the loop,
  // it means we can `return false` early and stop looping
  // otherwise if the loop finished without finding any non-matches,
  // then we will return true
  return false;
}
如果我们真的有兴趣比较字符串的内容,那么

length不是比较字符串的好方法。当然,在某些情况下,这是一个false值的捷径,但这是一个不成熟的优化。

从描述您意图的 minimal 程序开始。如果您稍后对代码进行分析并了解可以在您的函数中进行改进,然后您可以编写此类检查,并确保发表评论为何添加了length比较

但在你需要之前,请将其删除。只会让你更多地思考/担心,这会让你更加努力。

fixer upper

我并不是说残酷,但我们只是取消了大部分功能。我们将挽救我们能够并从那里开展工作

function isMerge(s, part1, part2) {
  for (let i = 0, len = s.length; i < len; i++) {
    if (s[i] ...)
      return false
  }
  return true;
}

因此,在该循环中,我们希望搜索s[i]part1part2中的第一个字母匹配的位置。但并不总是第一个字母,对吗?一旦我们匹配第1部分中的字母,我们就无法重复使用该字母。

我将添加两个变量,用于跟踪part1part2中当前未选中的位置。当任一部分匹配时,我们将提升相应变量的位置。

function isMerge(s, part1, part2) {
  // i keeps track of position in s
  // p1 keeps track of position in part1
  // p2 keeps track of position in part2
  for (let i = 0, len = s.length, p1 = 0, p2 = 0; i < len; i++) {
    // check current letter for match in part1
    if (s[i] === part1[p1]) {
      p1++     // increment p1 so we don't reuse this char
      continue // move on to next letter in s
    }
    // check current letter for match in part2
    else if (s[i] === part2[p2]) {
      p2++     // increment p1 so we don't reuse this char
      continue // move on to next letter in s
    }
    // current letter didn't match part1 or part2 !
    else {
      return false
    }
  }
  // we got thru the loop without resulting in false
  // so it must be true that s isMerge of part1 and part2
  return true
}


// example 1
{
  let str = 'codewars'
  let part1 = 'cdw'
  let part2 = 'oears'
  console.log(isMerge(str, part1, part2)) // true
}

// example 2
{
  let str = 'codewars'
  let part1 = 'cdb'
  let part2 = 'oears'
  console.log(isMerge(str, part1, part2)) // false
}

但实际上,我觉得这个解决方案难以推理。如果您剖析该函数的范围,您将看到我们需要跟踪的许多变量:spart1part2p1,{ {1}},p2i。更不用说lenip1需要在循环运行时进行更改,因此您必须确保完全正确。这需要管理很多。

我们在那里有一些评论来帮助读者理解我们的意图,但代码应该表达我们的意图,而不是反过来。事实上,我认为评论通常会使代码更难阅读,因为代码的方式更加残缺。

有一种更好的方法......

更好的方法

嘿,看!我们已经达到了更好的方式。那很快。

这是一个基本的递归过程,会检查p2的首字母对sx的第一个字母。如果两者匹配,则使用匹配字符串中的剩余字母进行递归。否则返回false。

我们使用其他几个小程序来帮助我们更有效地描述我们的计划。您会看到此代码中没有任何注释,并且它仍然具有可读性 - 这次代码执行解释...

y

失败

显然,您已经找到了一些条件,根据这些条件,我的最后一段代码无法满足您在原始帖子中未提及的某些条件。

我在代码大战上找到了测验,而这里的代码最失败的地方

const shead = s => s.substr(0,1)
const stail = s => s.substr(1)
const isEmpty = s => s === ''
const firstCharEqual = (x,y) => shead(x) === shead(y)

const isMerge = (s,x,y) => {
  if (isEmpty(s))
    return isEmpty(x) && isEmpty(y)
  else if (firstCharEqual(s, x) && firstCharEqual(s, y))
    return isMerge(stail(s), stail(x), y) || isMerge(stail(s), x, stail(y))
  else if (firstCharEqual(s, x))
    return isMerge(stail(s), stail(x), y)
  else if (firstCharEqual(s, y))
    return isMerge(stail(s), x, stail(y))
  else
    return false
}

// example 1; correct inputs; should be true
{
  let str = 'codewars'
  let part1 = 'cdw'
  let part2 = 'oears'
  console.log(isMerge(str, part1, part2)) // true
}

// example 2; mismatched input; should be false
{
  let str = 'codewars'
  let part1 = 'cdb'
  let part2 = 'oears'
  console.log(isMerge(str, part1, part2)) // false
}

// example 3; extra input; should be false (apparently)
{
  let str = 'codewars'
  let part1 = 'cdw_'
  let part2 = 'oears'
  console.log(isMerge(str, part1, part2)) // false
}
 
// example 3; extra input; should be false (apparently)
{
  let str = 'Bananas from Bahamas'
  let part1 = 'Bahas'
  let part2 = 'Bananas from am'
  console.log(isMerge(str, part1, part2)) // true
}

我已经编辑了上面的代码......

let s = 'Bananas from Bahamas',
let x = 'Bahas'
let y = 'Bananas from am'
isMerge(s,x,y); // should be true

...到......

if (isEmpty(s) && isEmpty(x) && isEmpty(y))
  return true

这将确保if (isEmpty(s)) return isEmpty(x) && isEmpty(y) else if (firstCharEqual(s, x) && firstCharEqual(s, y)) return isMerge(stail(s), stail(x), y) || isMerge(stail(s), x, stail(y)) part1在消耗part2的同时被完全消耗(清空)。

更重要的是,它将确保如果spart1同时匹配,我们会对递归进行分叉,并检查fork的任一分支是否导致part2

编辑后的代码通过了所有测试

答案 1 :(得分:0)

尝试类似:

function mergeCheck(s, p1, p2, i, j, k) {
    if (i == s.length) {
        return true;
    } else {
        if (s[i] == p1[j]) {
            j++;
        } else if (s[i] == p2[k]) {
            k++;
        } else {
            return false;
        }
        i++;
        return mergeCheck(s, p1, p2, i, j, k);
    }
}

function isMerge(s, p1, p2) {
    if (s.length == (p1.length + p2.length)) {
        return mergeCheck(s, p1, p2, 0, 0, 0);
    }
    else {
    	return false;
    }
}
console.log(isMerge("codewars", "cdw", "oears"))
console.log(isMerge("codewars", "cw", "oears"))
console.log(isMerge("codewars", "cdwx", "oears"))

s字符串的开头开始,将有问题的字母与p1p2的顺序字母进行比较。如果在其中任何一个中找到匹配,它会向前移动并检查后续字母,直到找到不匹配或到达s的末尾

答案 2 :(得分:-1)

function isMerge(s, part1, part2) {
            if (part1.length === 0) {
                return (s == part2);
            }
            if (part2.length === 0) {
                return (s == part1);
            }
            if (s.length === 0) {
                return false;
            } /*because if you are still here, part 1 and part 2 are not empty*/
            /*handle empty strings first both as "base case" and to not have to worry about expressions like s[0]*/
            return ((s[0] == part1[0]) && isMerge(s.substr(1), part1.substr(1), part2)) || ((s[0] == part2[0]) && isMerge(s.substr(1), part1, part2.substr(1)));
        }
        // example 1
        {
            let str = 'codewars'
            let part1 = 'cdw'
            let part2 = 'oears'
            console.log(isMerge(str, part1, part2)) // true
        }

        // example 2
        {
            let str = 'codewars'
            let part1 = 'cdb'
            let part2 = 'oears'
            console.log(isMerge(str, part1, part2)) // false
        }

答案 3 :(得分:-1)

这是一种非递归方法。

function checkIt (str, str1, str2){
    if(str.length !== str1.length + str2.length){
        return false;
    }

    var str1_index = -1
    var str2_index = -1;
    var curr_index1, curr_index2;

    for(var i=0; i<str.length; i++){
        curr_index1 = str1.indexOf(str[i]);
        curr_index2 = str2.indexOf(str[i]);

        if(curr_index1 == -1 && curr_index2 == -1){
            return false;
        }

        if(curr_index1 > str1_index){
            str1_index = curr_index1;
        }
        else if(curr_index1 !== -1 && curr_index1 < str1_index) {
            return false;
        }

        if(curr_index2 > str2_index){
            str2_index = curr_index2;
        }
        else if(curr_index2 !== -1 && curr_index2 < str2_index) {
            return false;
        }   
    }
    return true;
}


console.log(checkIt('codewars', 'cdw', 'oears')); // true
console.log(checkIt('codewars', 'cdw', 'ears'));  // false
console.log(checkIt('codewars', 'cwd', 'oears')); // false
console.log(checkIt('codewars', 'cdw', 'oeasr')); // false