【每日一题】最长公共前缀解题思路及解题技巧

示例 1:

输入: ["flower","flow","flight"]
输出: "fl"
复制代码

示例 2:

输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
复制代码

说明:

所有输入仅包含大写字母 a-z。

解决方案一:一一比较

解题思路:从前面的字符串比较中获取公共前缀

画图帮助理解:

代码实现:

var longestCommonPrefix = function(strs) {
    if (strs === null || strs.length === 0) return "";
    let prevs = strs[0]
    for(let i = 1; i < strs.length; i++) {
        let j = 0
        for(; j < prevs.length && j < strs[i].length; j++) {
            if(prevs.charAt(j) !== strs[i].charAt(j)) break
        }
        prevs = prevs.substring(0, j)
        if(prevs === "") return ""
    }
    return prevs
};
复制代码

时间复杂度:O(s),s是所有字符串的字符数之和

空间复杂度:O(1)

方案2:只需要最大和最小字符串的最长公共前缀

解决思路:获取链表中的最大最小字符串。最小字符串和最大字符串的最长公共前缀也是其他字符串的公共前缀,也就是字符串字段的最长公共前缀。

比如abc,abcd,ab,ac,最小ab和最大ac的最长公共前缀也必须是abc,abcd的公共前缀

画图帮助理解:

代码实现:

var longestCommonPrefix = function(strs) {
    if (strs === null || strs.length === 0) return "";
    if(strs.length === 1) return strs[0]
    let min = 0, max = 0
    for(let i = 1; i  strs[i]) min = i
        if(strs[max] < strs[i]) max = i
    }
    for(let j = 0; j < strs[min].length; j++) {
        if(strs[min].charAt(j) !== strs[max].charAt(j)) {
            return strs[min].substring(0, j)
        }
    }
    return strs[min]
};
复制代码

时间复杂度:O(n+m)向有限的空间输入超长的字符串是哪一种攻击手段,n为链表的宽度,m为字符串链表中最短字符的宽度

空间复杂度:O(1)

解决方案 3:分而治之的战略和融合理念

分而治之,顾名思义就是分而治之,把一个复杂的问题分成两个或多个相似的子问题,把子问题再分成更小的子问题,直到更小的子问题可以被简单地解决和解决。子问题,原问题的解是子问题的解的组合。

这个问题是一个典型的分而治之的策略问题:

画图帮助理解:

以abc、abcd、ab、ac为例:

代码实现:

var longestCommonPrefix = function(strs) {
    if (strs === null || strs.length === 0) return "";
    return lCPrefixRec(strs)
};
// 若分裂后的两个数组长度不为 1,则继续分裂
// 直到分裂后的数组长度都为 1,
// 然后比较获取最长公共前缀
function lCPrefixRec(arr) {
  let length = arr.length
  if(length === 1) {
    return arr[0]
  }
  let mid = Math.floor(length / 2),
      left = arr.slice(0, mid),
      right = arr.slice(mid, length)
  return lCPrefixTwo(lCPrefixRec(left), lCPrefixRec(right))
}
// 求 str1 与 str2 的最长公共前缀
function lCPrefixTwo(str1, str2) {
    let j = 0
    for(; j < str1.length && j < str2.length; j++) {
        if(str1.charAt(j) !== str2.charAt(j)) {
            break
        }
    }
    return str1.substring(0, j)
}
复制代码

时间复杂度:O(s),s是所有字符串的字符数之和

空间复杂度:O(m*logn),n为链表的宽度向有限的空间输入超长的字符串是哪一种攻击手段,m为字符串链表中最长字符的宽度

解决方案 4:Trie 树(字典树)

Trie树,也称为字典树或前缀树,顾名思义,它是一种用于处理字符串匹配问题的数据结构,也是一种用于在集合中查找固定前缀字符串的数据结构。

解决思路:构建Trie树,字符串字段的最长公共序列是从根节点开始遍历树,直到:

到目前为止,传递的字符是字符串字段的最长公共前缀

画图帮助理解:

构建Trie树,以abc、abcd、ab、ac为例:

代码实现:

var longestCommonPrefix = function(strs) {
    if (strs === null || strs.length === 0) return "";
    // 初始化 Trie 树
    let trie = new Trie()
    // 构建 Trie 树
    for(let i = 0; i < strs.length; i++) {
        if(!trie.insert(strs[i])) return ""
    }
    // 返回最长公共前缀
    return trie.searchLongestPrefix()
};
// Trie 树
var Trie = function() {
    this.root = new TrieNode()
};
var TrieNode = function() {
    // next 放入当前节点的子节点
    this.next = {};
    // 当前是否是结束节点
    this.isEnd = false;
};
Trie.prototype.insert = function(word) {
    if (!word) return false
    let node = this.root
    for (let i = 0; i < word.length; i++) {
        if (!node.next[word[i]]) {
            node.next[word[i]] = new TrieNode()
        }
        node = node.next[word[i]]
    }
    node.isEnd = true
    return true
};
Trie.prototype.searchLongestPrefix = function() {
    let node = this.root
    let prevs = ''
    while(node.next) {
        let keys = Object.keys(node.next)
        if(keys.length !== 1) break
        if(node.next[keys[0]].isEnd) {
            prevs += keys[0]
            break
        }
        prevs += keys[0]
        node = node.next[keys[0]]
    }
    return prevs
}
复制代码

时间复杂度:O(s+m),s是所有字符串的字符数之和,m是字符串列表中最长字符的宽度,构建Trie树需要O(s) ,最长公共前缀查询操作的复杂度是O(m)

空间复杂度:构建 Trie 树的 O(s)

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 网站程序 【每日一题】最长公共前缀解题思路及解题技巧 https://www.wkzy.net/game/8507.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务