leetcode第14题

简介: 我们把原来的数组分成两部分,求出左半部分的最长公共前缀,求出右半部分的最长公共前缀,然后求出的两个结果再求最长公共前缀,就是最后的结果了。求左半部分的最长公共前缀,我们可以继续把它分成两部分,按照上边的思路接着求。然后一直分成两部分,递归下去。直到该部分只有 1 个字符串,那么最长公共子串就是它本身了,直接返回就可以了。

image.png

top14

解法一 垂直比较

我们把所有字符串垂直排列,然后一列一列的比较,直到某一个字符串到达结尾或者该列字符不完全相同。

下边看一下我的代码,看起来比较多

//这个函数判断 index 列的字符是否完全相同publicbooleanisSameAtIndex(String[] strs, intindex) {
inti=0;
while (i<strs.length-1) {
if (strs[i].charAt(index) ==strs[i+1].charAt(index)) {
i++;
        } else {
returnfalse;
        }
    }
returntrue;
}
publicStringlongestCommonPrefix(String[] strs) {
if (strs.length==0)
return"";
//得到最短的字符串的长度intminLength=Integer.MAX_VALUE;
for (inti=0; i<strs.length; i++) {
if (strs[i].length() <minLength) {
minLength=strs[i].length();
        }
    }
intj=0;
//遍历所有列for (; j<minLength; j++) {
//如果当前列字符不完全相同,则结束循环if (!isSameAtIndex(strs, j)) {
break;
        }
    }
returnstrs[0].substring(0, j);
}

下边看一下,官方的代码

publicStringlongestCommonPrefix(String[] strs) {
if (strs==null||strs.length==0) return"";
//遍历所有列for (inti=0; i<strs[0].length() ; i++){
charc=strs[0].charAt(i); // 保存 i 列第 0 行的字符便于后续比较//比较第 1,2,3... 行的字符和第 0 行是否相等for (intj=1; j<strs.length; j++) {
/*** i == strs[j].length() 表明当前行已经到达末尾* strs[j].charAt(i) != c  当前行和第 0 行字符不相等* 上边两种情况返回结果*/if (i==strs[j].length() ||strs[j].charAt(i) !=c)
returnstrs[0].substring(0, i);             
        }
    }
returnstrs[0];
}

时间复杂度:最坏的情况就是 n 个 长度为 m 的完全一样的字符串,假设 S 是所有字符的和,那么 S = m * n,时间复杂度就是 O(S)。当然正常情况下并不需要比较所有字符串,最多比较 n * minLen 个字符就可以了。

空间复杂度:O(1),常数个额外空间。

解法二 水平比较

我们将字符串水平排列,第 0 个和第 1 个字符串找最长子串,结果为 leet,再把结果和第 2 个字符串比较,结果为 leet,再把结果和第 3 个字符串比较,结果为 lee,即为最终结果。

publicStringlongestCommonPrefix3(String[] strs) {
if (strs.length==0)
return"";
Stringprefix=strs[0]; // 保存结果// 遍历每一个字符串for (inti=1; i<strs.length; i++) {
// 找到上次得到的结果 prefix 和当前字符串的最长子串intminLen=Math.min(prefix.length(), strs[i].length());
intj=0;
for (; j<minLen; j++) {
if (prefix.charAt(j) !=strs[i].charAt(j)) {
break;
                }
            }
prefix=prefix.substring(0, j);
        }
returnprefix;
    }

时间复杂度:最坏情况和解法一是一样,n 个长度为 m 的完全相同的字符,就要比较所有的字符 S,S = n * m 。但对于正常情况,处于最短字符串前的字符串依旧要比较所有字符,而不是最短字符串个字符,相对于解法一较差。

空间复杂度:O(1)。

解法三 递归

我们把原来的数组分成两部分,求出左半部分的最长公共前缀,求出右半部分的最长公共前缀,然后求出的两个结果再求最长公共前缀,就是最后的结果了。

求左半部分的最长公共前缀,我们可以继续把它分成两部分,按照上边的思路接着求。然后一直分成两部分,递归下去。

直到该部分只有 1 个字符串,那么最长公共子串就是它本身了,直接返回就可以了。

publicStringlongestCommonPrefix(String[] strs) {
if (strs==null||strs.length==0) return"";    
returnlongestCommonPrefix(strs, 0 , strs.length-1);
}
//递归不断分成两部分privateStringlongestCommonPrefix(String[] strs, intl, intr) {
if (l==r) {
returnstrs[l];
    }
else {
intmid= (l+r)/2;
StringlcpLeft=longestCommonPrefix(strs, l , mid);
StringlcpRight=longestCommonPrefix(strs, mid+1,r);
returncommonPrefix(lcpLeft, lcpRight);
   }
}
//求两个结果的最长公共前缀StringcommonPrefix(Stringleft,Stringright) {
intmin=Math.min(left.length(), right.length());       
for (inti=0; i<min; i++) {
if ( left.charAt(i) !=right.charAt(i) )
returnleft.substring(0, i);
    }
returnleft.substring(0, min);
}

时间复杂度:

空间复杂度:

每次遇到递归的情况,总是有些理不清楚,先空着吧。

进行了垂直比较和水平比较,又用到了递归,solution 里还介绍了二分查找,感觉这里用二分查找有些太僵硬了,反而使得时间复杂度变高了。还介绍了前缀树,这里后边遇到再总结吧。


相关文章
|
6月前
leetcode-472. 连接词
leetcode-472. 连接词
49 0
|
6月前
|
C++ Python
leetcode-283:移动零
leetcode-283:移动零
29 0
|
6月前
leetcode-1219:黄金矿工
leetcode-1219:黄金矿工
79 0
leetcode 827 最大人工岛
leetcode 827 最大人工岛
60 0
leetcode 827 最大人工岛
|
测试技术
一和零(LeetCode-474)
一和零(LeetCode-474)
136 0
一和零(LeetCode-474)
leetcode第55题
当自己按照 45 题的思路写完的时候,看 Solution 的时候都懵逼了,这道题竟然这么复杂?不过 Solution 把问题抽象成动态规划的思想,以及优化的过程还是非常值得学习的。
leetcode第55题
|
算法
leetcode第45题
时间复杂度:O(n)。 空间复杂度:O(1)。 这里要注意一个细节,就是 for 循环中,i < nums.length - 1,少了末尾。因为开始的时候边界是第 0 个位置,steps 已经加 1 了。如下图,如果最后一步刚好跳到了末尾,此时 steps 其实不用加 1 了。如果是 i < nums.length,i 遍历到最后的时候,会进入 if 语句中,steps 会多加 1 。
105 0
leetcode第45题
|
算法
leetcode第30题
如上图,利用循环变量 i ,依次后移,判断每个子串是否符合即可。 怎么判断子串是否符合?这也是这个题的难点了,由于子串包含的单词顺序并不需要固定,如果是两个单词 A,B,我们只需要判断子串是否是 AB 或者 BA 即可。如果是三个单词 A,B,C 也还好,只需要判断子串是否是 ABC,或者 ACB,BAC,BCA,CAB,CBA 就可以了,但如果更多单词呢?那就崩溃了。 链接)的作者提出了,用两个 HashMap 来解决。首先,我们把所有的单词存到 HashMap 里,key 直接存单词,value 存单词出现的个数(因为给出的单词可能会有重复的,所以可能是 1 或 2 或者其他)。然后扫描子
119 0
leetcode第30题
leetcode第31题
我们想几个问题。 要想使得数字变大,只要任意一位变大就可以。 要想得到刚好大于原来的数字,要变个位。 这里变大数字,只能利用交换。 如果从个位开始,从右往左进行,找一个比个位大的,交换过来,个位的数字交换到了更高位,由于个位的数字较小,所以交换过去虽然个位变大了,但数字整体变小了。例如 1 3 2,把 2 和 3 交换,变成 1 2 3,个位变大了,但整体数字变小了。 个位不行,我们再看十位,如果从十位左边找一个更大的数字交换过来,和个位的情况是一样的,数字会变小。例如 4 1 2 3,把 2 和 4 交换,2 1 4 3,数字会变小。如果从
leetcode第31题
leetcode第26题
for 循环遍历每个数,while 循环判断当前数和它的后一个数是否相等,相等就后移一个数,并且接着判断后移的数和它后边的数是否相等,然后一直循环下去。不相等就将后一个数保存起来,并且长度加 1,然后结束循环。
leetcode第26题