• LeetCode刷题--14.最长公共前缀(简单)


    题目描述

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

    如果不存在公共前缀,返回空字符串 ""

    示例 1:

    输入: ["flower","flow","flight"]
    输出: "fl"

    示例 2:

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

    说明:

    所有输入只包含小写字母 a-z 。

    方法一:水平扫描法

    思路:

    首先,我们将描述一种查找一组字符串的最长公共前缀LCP(S1..Sn)的简单方法。

    我们将会用到这样的结论

    LCP(S1. . .Sn) = LCP(LCP(LCP(S1,S2),S3). . .Sn)

    算法


    为了运用这种思想,算法要依次遍历字符串[S1...Sn],当遍历到第i个字符串的时候,找到最长公共前缀LCP(S1. . .Si)。当LCP(S1. . .S)是一个空串的时候,算法就结束了。否则,在执行了n次遍历之后,算法就会返回最终答案LCP(S1. . .Sn)

    代码实现

    class Solution {
        public String longestCommonPrefix(String[] strs) {
            //如果字符串数组长度为0,返回空字符串""
            if (strs.length == 0) return "";
            //定义字符串数组第一个字符串为prefix
            String prefix = strs[0];
            for (int i = 1; i < strs.length; i++) {
                //判断当前字符串strs[i]中是否有prefix(某个字符或某个字符串),
                // 有的话就返回所在的下标(首次出现的位置),没有的话就返回-1
                while (strs[i].indexOf(prefix) != 0) {
                    //prefix长度从后面减少一位
                    prefix = prefix.substring(0, prefix.length() - 1);
                    //如果不存在公共前缀,返回空字符串 ""
                    if (prefix.isEmpty()) return "";
                }
            }
            return prefix;
        }
    }

    方法二:xx扫描法

    算法

    想象数组的末尾有一个非常短的字符串,使用上述方法依旧会进行 S​​ 次比较。优化这类情况的一种方法就是水平扫描。我们从前往后枚举字符串的每一列,先比较每个字符串相同列上的字符(即不同字符串相同下标的字符)然后再进行对下一列的比较。

    代码实现

    class Solution {
        public String longestCommonPrefix(String[] strs) {
            //如果字符串数组等于空或长度为0,返回空字符串""
            if (strs == null || strs.length == 0) return "";
            //这一层for循环遍历的是第一个字符串里的第 i 个字符串,后面的遍历都是与第一个字符串比对
            for (int i = 0; i < strs[0].length() ; i++){
                //把第一个字符串第i个字符赋给c
                char c = strs[0].charAt(i);
                //这一层for循环遍历的是第j字符串,并且取第 i 个字符串
                for (int j = 1; j < strs.length; j ++) {
                    //如果i等于第j个字符串的长度 或 第j个字符串的第i个字符不等于c ,两者顺序不能颠倒
                    if (i == strs[j].length() || strs[j].charAt(i) != c)
                        //返回第一个字符串的0-i个字符
                        return strs[0].substring(0, i);
                }
            }
            return strs[0];
        }
    }

    算法三:分治思路

    这个算法的思路来自于LCP操作的结合律。我们可以发现:
    LCP(S1. . .Sn)= LCP(LCP(S1. . .SA),LCP(Sk + 1.Sn))
    ,其中LCP(S1. . .Sn)是字符串[S1..Sn]的最长公共前缀,1 < k < n。
    算法
    为了应用上述的结论,我们使用分治的技巧,将原问题LCP(S;…S;)分成两个子问题LCP(S;…Smid)与LCP(Smid + 1,S;),其中mid=i。我们用子问题的解1cpLeft 与1cpRi ght 构造原问题的解LCP(S;…S;)
    。从头到尾挨个比较 lcpLeft 与 lcpRight 中的字符,直到不能再匹配为止。计算所得的 lcpLeft与 lcpRight最长公共前缀就是原问题的解 LCP(S;…S;)。

     代码实现

    public String longestCommonPrefix(String[] strs) {
        //如果字符串数组等于空或长度为0,返回空字符串""
        if (strs == null || strs.length == 0) return "";
        return longestCommonPrefix(strs, 0, strs.length - 1);
    }
    
    private String longestCommonPrefix(String[] strs, int l, int r) {
        if (l == r) {
            return strs[l];
        } else {
            int mid = (l + r) / 2;
            //先从左半部分遍历,遍历的都是整个字符串,直到l等于r 返回最左边的字符串
            String lcpLeft = longestCommonPrefix(strs, l, mid);
    
            //遍历左半部分的右边,直到l等于r 返回左半部分右边的的字符串
            String lcpRight = longestCommonPrefix(strs, mid + 1, r);
    
            //拿到两个字符串之后,进行比较,找公共字首
            return commonPrefix(lcpLeft, lcpRight);
        }
    }
    
    String commonPrefix(String left, String right) {
        //取最短的那个字符串的长度,最好的情况是最短字符串全部都是最长公共前缀
        int min = Math.min(left.length(), right.length());
        for (int i = 0; i < min; i++) {
            if (left.charAt(i) != right.charAt(i))
                //substring(截取字符串)包括左边界,但却不包括右边界
                return left.substring(0, i);
        }
        return left.substring(0, min);
    }

    方法四:二分查找法

    思路

    这个想法是应用二分查找法找到所有字符串的公共前缀的最大长度L。算法的查找区间是(0. . .minLen),其中minLen是输入数据中最短的字符串的长度,同时也是答案的最长可能长度。

    每一次将查找区间一分为二,然后丢弃一定不包含最终答案的那一个。算法进行的过程中一共会出现两种可能情况:

    • S[1. . .mid]不是所有串的公共前缀。这表明对于所有的 j > i S[ 1. .j ]也不是公共前缀,于是我们就可以丢弃后半个查找区间。
    • S[1. . .mid]是所有串的公共前缀。这表示对于所有的 i < j S[ 1. .i ]都是可行的公共前缀,因为我们要找最长的公共前缀,所以我们可以把前半个查找区间丢弃。

     代码实现

    class Solution {
        public String longestCommonPrefix(String[] strs) {
            if (strs == null || strs.length == 0) {
                return "";
            }
            //定义minLen初始值为Integer的最大值
            int minLen = Integer.MAX_VALUE;
            //遍历数组strs
            for (String str : strs) {
                //取最小的字符串长度,因为最好的情况就是全匹配最小字符串
                minLen = Math.min(minLen, str.length());
            }
            int low = 1;
            int high = minLen;
            while (low <= high) {
                //二分法,取最短字符串一半
                int middle = (low + high) / 2;
                //进行左右比较
                if (isCommonPrefix(strs, middle)) {
                    //在字符串是以str1为前缀开始的时候,low=middle+1是多遍历一个字符位置
                    low = middle + 1;
                } else {
                    //字符串不是以str1为前缀开始的时候,high=midd-1,是少遍历一个字符位置
                    high = middle - 1;
                }
            }
            //返回的是 截取第一个字符串0到mid字符串
            return strs[0].substring(0, (low + high) / 2);
        }
    
        private boolean isCommonPrefix(String[] strs, int len){
            //截取第一个字符串的前len个字符
            String str1 = strs[0].substring(0,len);
            for (int i = 1; i < strs.length; i++) {
                //startsWith() 方法用于检测字符串是否以str1为前缀开始,如果不是直接return false
                if (!strs[i].startsWith(str1)) {
                    return false;
                }
            }
            return true;
        }
    }
  • 相关阅读:
    POJ 3468 A Simple Problem with Integers(线段树 区间更新)
    Windows Mobile 6.0 SDK和中文模拟器下载
    Linux学习笔记——例说makefile 头文件查找路径
    uva 11427
    腾讯2014年实习生招聘笔试面试经历
    AVC1与H264的差别
    oracle递归函数
    全部编程皆为Web编程
    JavaScript--语法2--语句结构
    JavaScript--变量和运算符
  • 原文地址:https://www.cnblogs.com/xiaozhongfeixiang/p/12048918.html
Copyright © 2020-2023  润新知