LeetCode初級算法之字符串:14 最長公共前綴

最長公共前綴

題目地址:https://leetcode-cn.com/problems/longest-common-prefix/java

編寫一個函數來查找字符串數組中的最長公共前綴。算法

若是不存在公共前綴,返回空字符串 ""。數組

示例 1:數據結構

輸入: ["flower","flow","flight"]
輸出: "fl"函數

示例 2:優化

輸入: ["dog","racecar","car"]
輸出: ""
解釋: 輸入不存在公共前綴。code

說明:遞歸

全部輸入只包含小寫字母 a-z 。索引

解法一:橫向比較

去找到多個串的公共前綴不知道,但咱們至少知道找兩個串的公共前綴。因而兩兩一組用上次公共串找下公共直到n-1次迭代完成最終公共前綴,那麼像第一個示例三個串,就須要2次迭代leetcode

也就是說咱們用一個公共前綴與下一個獲得公共前綴而後更新覆蓋,開始下一次迭代。那麼一開始咱們指定strs[0]爲第一代的公共前綴,下面是初次提交成功的代碼:

public String longestCommonPrefix(String[] strs){
    int n = strs.length;
    if(strs.length == 0) return "";
    //最初公共前綴
    String common = strs[0];
    //兩兩一次共n-1次迭代
    for(int i = 1; i < n; i++){
        //一次迭代的過程
        int j = 0;
        char[] cur = common.toCharArray();
        char[] next = strs[i].toCharArray();
        //strs數組與兩個字符串邊界
        if(next.length == 0) return "";
        while( j < cur.length && j < next.length ){
            // 掃描到不一樣了,那麼這個索引在最後一個相同的後面一個
            if(cur[j] != next[j]){
                common = common.substring(0,j);//注意區間左閉右開
                break;
            // 沒有掃描到不一樣但但已經結尾了,那麼索引就是相同的最後一個
            }else if(j == cur.length - 1 || j == next.length - 1){
                common = common.substring(0,j+1);
                break;
            }
            j++;
        }
    }
    return common;
}

但這段代碼是存在問題的,咱們要考慮的問題是在循環裏面的操做能不能減小,雖然時間複雜度不會變化。但減小循環體的操做也能夠成倍的提高,觀察代碼首次迭代多少次就一個for循環沒有什麼問題,那麼重點關注的是一次迭代這個過程的代碼。咱們這段代碼拿出來:

while( j < cur.length && j < next.length ){
    // 掃描到不一樣了,那麼這個索引在最後一個相同的後面一個
    if(cur[j] != next[j]){
        common = common.substring(0,j);//注意區間左閉右開
        break;
    // 沒有掃描到不一樣但但已經結尾了,那麼索引就是相同的最後一個
    }else if(j == cur.length - 1 || i == next.length - 1){
        common = common.substring(0,i+1);
        break;
    }
    j++;
}

先看循環體,由於兩段if都有break,那麼有三段只能走一個。咱們想想兩段if的操做是否是能夠合併的。聰明的小夥伴應該能很快反應過來,無非是有木有掃描超過致使是截取[ 0 , j )和[ 0 , j+1 ),那麼其實只要不知足兩個相等就出循環那麼索引都是在最後相等後加了1,根本不用判斷截取[ 0 , j )便可。代碼馬上能夠減小一部分

while( j < cur.length && j < next.length && cur[j] == next[j]){
    j++;
}
common = common.substring(0,j);//注意區間左閉右開

同時它也減小了咱們在循環裏的操做。若是循環是n次,那麼裏面有2-3次操做那麼次數就是2-3n,儘可能去減小裏層循環的操做次數。除了循環體以外while條件也是隨着循環被執行由於是短路與可能執行一個結束也可能執行三個式子,因此咱們能夠減小一次

int length = cur.length > next.length ? next.length : cur.length;
while( j < length && cur[j] == next[j]){
    j++;
}
common = common.substring(0,j);//注意區間左閉右開

這就至關於咱們往外層添加了一次操做但減小了循環的一次操做,這些其實優化都不大可是在不換解題思惟以及數據結構等等咱們能夠去考慮的優化點

public String longestCommonPrefix(String[] strs){
    int n = strs.length;
    if(strs.length == 0) return "";
    //最初公共前綴
    String common = strs[0];
    //兩兩一次共n-1次迭代
    for(int i = 1; i < n; i++){
        //一次迭代的過程
        int j = 0;
        char[] cur = common.toCharArray();
        char[] next = strs[i].toCharArray();
        //strs數組與兩個字符串邊界
        if(next.length == 0) return "";
        int length = cur.length > next.length ? next.length : cur.length;
        while( j < length && cur[j] == next[j]){
            j++;
        }
        common = common.substring(0,j);//注意區間左閉右開
    }
    return common;
}

解法二:縱向比較

其實最開始我想到的解法就是縱向比較同時掃描多個串每一個串的字符都相等再同時日後掃描直到有第一個不一樣就終止。

但當時只有一個直觀的想法想着一字排開比較相等而後感受處理不了就想到橫向的比較好寫

int i = 0;
while( i < min(length) && str1[i] == str2[i]==..... ){
    i++;
}

其實這裏取一個字符串遍歷,在一次遍歷裏面遍歷數組的其餘的字符串都進行比較便可

public String longestCommonPrefix(String[] strs) {
    //爲空返回""
    if (strs.length == 0) return "";
    //取數組減小charAt在循環體
    char[] p = str[0].toCharArray();
    int n = p.length;
    for (int i = 0; i < n; i++) {
        char c = p[i];
        //依次判斷直到不等出現,截取返回
        for (int j = 1; j < strs.length; j++) {
            if (i == strs[j].length() || strs[j].charAt(i) != c) {
                return strs[0].substring(0, i);
            }
        }
    }
    return strs[0];
}

解法三:變體(解一)

咱們出了經過循環掃描比較獲得兩串的公共前綴,還能夠經過前綴是否包含能夠用a.startsWith(b)判斷是否以起始索引包含另外一個串,或者用a.indexOf(b) == 0 來判斷,沒有則刪減一位直到找到公共前綴開始下個迭代和解一是同樣的只是找公共前綴的方式不一樣

public String longestCommonPrefix(String[] strs) {
    //爲空返回""
    if (strs.length == 0) return "";
    String common = strs[0];
    for (String str : strs) {
        // 若common已經減爲"",則說明無公共前綴,直接返回
        if (common == "") return common;
        // 若common在當前str中匹配不上,則減小字符串common的長度,再次嘗試匹配
        while (!str.startsWith(common)) {
            common = common.substring(0,common.length() - 1);
        }
    }
    return common;
}

總結

整體而言的話其實時間複雜度都是比較高的,畢竟都使用了substring等方法超過n的三次方都差很少因此沒有較優解。也是和上一題差很少兩題都是體會迭代的一個過程所以也均可以轉成遞歸的形式,那就還能夠分治分到最後都是兩兩一組而後求解自底向上以後會介紹,到此LeetCode初級算法合集中的字符串系列所有完成,開啓下個篇章鏈表。

相關文章
相關標籤/搜索