Javascript 中最长的公共前缀

Posted

技术标签:

【中文标题】Javascript 中最长的公共前缀【英文标题】:Longest Common Prefix in Javascript 【发布时间】:2021-10-12 14:22:35 【问题描述】:

我正在尝试解决 Leet Code 挑战14. Longest Common Prefix:

编写一个函数来查找字符串数组中最长的公共前缀字符串。

如果没有公共前缀,则返回空字符串""

示例 1:

Input: strs = ["flower","flow","flight"]
Output: "fl"

示例 2:

Input: strs = ["dog","racecar","car"]
Output: ""

说明:输入字符串之间没有共同的前缀。

约束:

1 <= strs.length <= 200 0 <= strs[i].length <= 200 strs[i] 仅由小写英文字母组成。

我的解决方案:

let strs = ["flower", "flow", "flight"];

var longestCommonPrefix = function (strs) 
  for (let i = 0; i < strs.length; i++) 
    for (let j = 0; j < strs[i].length; j++) 
       // console.log(strs[i+2][j]);
      if (strs[i][j] == strs[i + 1][j] && strs[i + 1][j] ==strs[i + 2][j]) 
        return (strs[i][j]);
        
       else 
        return "0";
      
    
  
;

console.log(longestCommonPrefix(strs));

输出:f

如何遍历每个字符并检查它是否相同然后继续下一步,如果失败则返回最长的公共前缀?

【问题讨论】:

如果您不知道哪里出了问题:您的第一个行动方案是找出哪里出了问题。您可能无法弄清楚如何修复它,但是您确实需要投入时间和精力来调试自己的代码:使用少量作为输入并放置一些控制台登录您的循环,甚至更好,但在其中使用debugger 语句并使用浏览器的调试器在观察变量的同时单步执行您的代码。当您知道每次迭代应该发生什么时,您可以很快发现问题所在。另外,在 JS 注释上:不要使用 var x = function... ,只需声明一个函数。 我已经完成了你提到的这些事情,也得到了为什么我得到那个输出的答案。问题是如何检查每个字符并返回最长的公共前缀? 然后你会想update your post 解释你发现了什么,你发现哪里出了问题,以及你已经检查过的有希望(但没有成功)解决问题的地方。 【参考方案1】:

由于最长的公共前缀必须出现在数组的每个字符串中,您可以迭代长度并检查所有单词在该索引处是否具有相同的字符,直到找到差异

function prefix(words)
  // check border cases size 1 array and empty first word)
  if (!words[0] || words.length ==  1) return words[0] || "";
  let i = 0;
  // while all words have the same character at position i, increment i
  while(words[0][i] && words.every(w => w[i] === words[0][i]))
    i++;
  
  // prefix is the substring from the beginning to the last successfully checked i
  return words[0].substr(0, i);


console.log(1, prefix([]));
console.log(2, prefix([""]));
console.log(3, prefix(["abc"]));
console.log(4, prefix(["abcdefgh", "abcde", "abe"]));
console.log(5, prefix(["abc", "abc", "abc"]));
console.log(6, prefix(["abc", "abcde", "xyz"]));

【讨论】:

非常感谢您的帮助。你能告诉我这个解决方案的时间复杂度是多少吗?因为我也做过同样的事情,但时间复杂度为 O(n2)。 let strs = ["flower", "flow", "floght"]; var longestCommonPrefix = function (strs) for (let i = 0; i &lt; strs.length; i++) for (let j = 0; j &lt; strs[i].length; j++) while(strs[i][j] == strs[i + 1][j] &amp;&amp; strs[i][j] == strs[i + 2][j]) j++; return String(strs).substr(0,j); console.log(longestCommonPrefix(strs)); 复杂度为O(n * m),其中n是数组中的单词数,m是前缀长度 而且,在第一个if 语句中,它像Conditional (ternary) operator 吗?如果可能的话,请您解释一下或分享我可以了解的文档吗? 不,这与三元运算符无关。在 words[0] || "" 中,这是一个简单的 OR || 运算符。即,如果第一个操作数 (words[0]) 是 truthy,则返回第一个操作数。如果是falsy,则返回第二个 ...【参考方案2】:

一些问题:

您的内部循环将在其第一次迭代中遇到return。这意味着您的循环将永远不会重复,并且返回值将始终是一个字符。

在循环中寻址 strs[i+1]strs[i+2] 是错误的,因为这些索引会超出范围 (&gt;= strs.length)

您可以使用子字符串(前缀)比较(在一个操作中),而不是逐个字符地进行比较:这可能看起来很浪费,但是由于这种比较发生在 javascript 代码“下方”,它非常快(并且作为字符串大小限制为 200 个字符,这很好)。

该算法可以从选择一个现有字符串作为前缀开始,然后每次输入中有一个没有它作为前缀的字符串时缩短它。最后,您将留下通用前缀。

最好以 shortest 字符串作为初始前缀候选,因为公共前缀肯定不能比这更长。

var longestCommonPrefix = function(strs) 
    let prefix = strs.reduce((acc, str) => str.length < acc.length ? str : acc);
    
    for (let str of strs) 
        while (str.slice(0, prefix.length) != prefix) 
            prefix = prefix.slice(0, -1);
        
    
    return prefix;
;

let res = longestCommonPrefix(["flower","flow","flight"]);

console.log(res);

【讨论】:

我猜你正在从数组中获取前两个字符串。那么如果第三个字符串的长度小于其他两个呢?而且,如果可能的话,你能解释一下这条线是什么意思for(let str of strs)吗? 第一行迭代整个数组(使用reduce)以找到最短的字符串。请参阅mdn documentation 中的for...of。它只是对数组中所有值的循环,无需维护索引变量。 迄今为止发布的最佳解决方案,干得好! PS 此代码因空数组而崩溃,但如果这是正确的行为,可能还有待商榷;) 在数组的大小至少为 1 的条件下,我没有费心处理空数组。【参考方案3】:

不是最好的解决方案,但这应该可行

function longestPrefix(strs)
    if(strs.length <1)
        return "";
    
    const sharedPrefix=function(str1,str2)
        let i=0;
        for(;i<Math.min(str1.length,str2.length) /*todo optimize*/;++i)
            if(str1[i] !== str2[i])
                break;
            
        
        return str1.substr(0,i);
    ;
    let curr = strs[0];
    for(let i=1;i<strs.length;++i)
        curr=sharedPrefix(curr,strs[i]);
        if(curr.length < 1)
            // no shared prefix
            return "";
        
    
    return curr;

【讨论】:

【参考方案4】:

一种基于字长排序的方法,对于最短的单词,为了提前退出,完全基于Array.every的前缀验证和聚合...

function longestCommonPrefix(arr) 
  const charList = [];

  const [shortestWord, ...wordList] =
    // sort shallow copy by item `length` first.
    [...arr].sort((a, b) => a.length - b.length);

  shortestWord
    .split('')
    .every((char, idx) => 
      const isValidChar = wordList.every(word =>
        word.charAt(idx) === char
      );
      if (isValidChar) 
        charList.push(char);
      
      return isValidChar;
    );

  return charList.join('');


console.log(
  longestCommonPrefix(["flower","flow","flight"])
);
.as-console-wrapper  min-height: 100%!important; top: 0; 

【讨论】:

【参考方案5】:

这个:

strs[i][j] == strs[i + 1][j] ==strs[i + 2][j]

在 JS 中没有意义......或者至少,你正在做的事情没有意义......要做到这一点,你应该使用 &amp;&amp; 运算符,如下所示:

strs[i][j] == strs[i + 1][j] && strs[i + 1][j] ==strs[i + 2][j]

否则 JS 将评估第一个条件,然后将评估该操作的结果(truefalse)与第三个值

除此之外,考虑到您在数组上循环使用i,因此i 也将是str.length - 1,但在您引用strs[i + 2][j] 的情况下,在这种情况下将是@987654330 @在你的情况下,没有意义。

关于解决方案: 您应该考虑前缀对于数组中的所有值都是通用的,因此您可以考虑一个值,然后检查所有其他值是否相等......最明显的是第一个,您最终会像这样:

let strs = ["flower", "flow", "flight", "dix"];

function longestCommonPrefix (strs) 
  // loop over the characters of the first element
  for (let j = 0; j < strs[0].length; j++) 
    // ignore the first elements since is obvious that is equal to itself
    for (let i = 1; i < strs.length; i++) 
       /* in case you have like 
        [
          'banana',
          'bana'
        ]
        the longest prefix is the second element
       */
       if(j >= strs[i].length)
         return strs[i]
       
       // different i-th element
       if(strs[0][j] != strs[i][j])
         return strs[0].substr(0, j)
       
       
    
  
  // all good, then the first element is common to all the other elements
  return strs[0]
;

console.log(longestCommonPrefix(strs));

【讨论】:

解释 why 没有意义(因为第一个 == 将产生一个布尔值,所以第二个 == 将始终为假)将是一个很好的编辑。 @Alberto Sinigaglia 非常感谢您的帮助,我应该在代码中进行哪些更改,以便可以迭代下一个字符检查?也请看看这个问题。 @Mike'Pomax'Kamermans 非常感谢您的解释。 这不是一个正确的解决方案。一方面,它可能在查看所有字符串之前返回......例如,["aaa", "aaaaaa", "what"] 应该返回一个空字符串,但此代码返回 "aa"。 @HarshMishra 请注意有一个错字...顺便说一句,我建议您检查一下非常简洁的 derpirscher 答案【参考方案6】:
let strs = ["flower", "flow", "flight"];

var longestCommonPrefix = function (strs) 
  for (let i = 0; i < strs.length; i++) 
    for (let j = 0; j < strs[i].length; j++) 
        console.log(strs[i+2][j]);
      if (strs[i][j] == strs[i + 1][j] && strs[i][j]  ==strs[i + 2][j]) 
        return (strs[i][j]);
        


       else 
        return "0";
      
    
  
;

console.log(longestCommonPrefix(strs));

这个**返回** f

【讨论】:

【参考方案7】:

当列表中所有单词的索引处的字母相同时,增加索引。然后切片。

function prefix(words) 
  if (words.length === 0)  return '' 
  let index = 0;
  while (allSameAtIndex(words, index)) 
    index++;
  
  return words[0].slice(0, index);


function allSameAtIndex(words, index) 
  let last;
  for (const word of words) 
    if (last !== undefined && word[index] !== last[index]) 
      return false;
    
    last = word;
  
  return true;

【讨论】:

【参考方案8】:

我假设你是来解决 Leetcode 问题的。

var longestCommonPrefix = function(strs) 
let arr = strs.concat().sort();
const a1 = arr[0];
const a2 = arr[arr.length -1];
const length = a1.length;
let i=0;

while(i<length && a1.charAt(i) == a2.charAt(i)) i++;
return a1.substring(0,i);

;

【讨论】:

【参考方案9】:

function prefixLen(s1, s2) 
    let i = 0;
    while (i <= s1.length && s1[i] === s2[i]) i++;
    return i;


function commonPrefix(arr) 
    let k = prefixLen(arr[0], arr[1]);
    for (let i = 2; i < arr.length; i++) 
        k = Math.min(k, prefixLen(arr[0], arr[i]));
    
    return arr[0].slice(0, k);


console.log(commonPrefix(['pirate', 'pizza', 'pilates']))  // -> "pi"

【讨论】:

【参考方案10】:
var longestCommonPrefix = function(strs) 
    let prefix = "";
    for(let i = 0; i < strs[0].length; i++) 
        for(let j = 1; j < strs.length; j++) 
            if(strs[j][i] !== strs[0][i]) return prefix;
        
        prefix = prefix + strs[0][i];
    
    return prefix;
;
console.log(longestCommonPrefix);

【讨论】:

你能解释一下你做了什么改变以及为什么你的答案会解决这个问题吗?

以上是关于Javascript 中最长的公共前缀的主要内容,如果未能解决你的问题,请参考以下文章

题14:最长公共前缀

2021-09-15:最长公共前缀。编写一个函数来查找字符串数组中的最长公共前缀,如果不存在公共前缀,返回空字符串 ““。力扣14。

14.最长公共前缀

leetcode-最长公共前缀

Haskell 中最长的公共前缀

最长的公共前缀