leetcode.windliang.cc/ 第一時間釋出
題目描述(簡單難度)
解法一 垂直比較
我們把所有字串垂直排列,然後一列一列的比較,直到某一個字串到達結尾或者該列字元不完全相同。
下邊看一下我的程式碼,看起來比較多
//這個函式判斷 index 列的字元是否完全相同
public boolean isSameAtIndex(String[] strs, int index) {
int i = 0;
while (i < strs.length - 1) {
if (strs[i].charAt(index) == strs[i + 1].charAt(index)) {
i++;
} else {
return false;
}
}
return true;
}
public String longestCommonPrefix(String[] strs) {
if (strs.length == 0)
return "";
//得到最短的字串的長度
int minLength = Integer.MAX_VALUE;
for (int i = 0; i < strs.length; i++) {
if (strs[i].length() < minLength) {
minLength = strs[i].length();
}
}
int j = 0;
//遍歷所有列
for (; j < minLength; j++) {
//如果當前列字元不完全相同,則結束迴圈
if (!isSameAtIndex(strs, j)) {
break;
}
}
return strs[0].substring(0, j);
}
複製程式碼
下邊看一下,官方的程式碼
public String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) return "";
//遍歷所有列
for (int i = 0; i < strs[0].length() ; i++){
char c = strs[0].charAt(i); // 儲存 i 列第 0 行的字元便於後續比較
//比較第 1,2,3... 行的字元和第 0 行是否相等
for (int j = 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)
return strs[0].substring(0, i);
}
}
return strs[0];
}
複製程式碼
時間複雜度:最壞的情況就是 n 個 長度為 m 的完全一樣的字串,假設 S 是所有字元的和,那麼 S = m * n,時間複雜度就是 O(S)。當然正常情況下並不需要比較所有字串,最多比較 n * minLen 個字元就可以了。
空間複雜度:O(1),常數個額外空間。
解法二 水平比較
我們將字串水平排列,第 0 個和第 1 個字串找最長子串,結果為 leet,再把結果和第 2 個字串比較,結果為 leet,再把結果和第 3 個字串比較,結果為 lee,即為最終結果。
public String longestCommonPrefix3(String[] strs) {
if (strs.length == 0)
return "";
String prefix = strs[0]; // 儲存結果
// 遍歷每一個字串
for (int i = 1; i < strs.length; i++) {
// 找到上次得到的結果 prefix 和當前字串的最長子串
int minLen = Math.min(prefix.length(), strs[i].length());
int j = 0;
for (; j < minLen; j++) {
if (prefix.charAt(j) != strs[i].charAt(j)) {
break;
}
}
prefix = prefix.substring(0, j);
}
return prefix;
}
複製程式碼
時間複雜度:最壞情況和解法一是一樣,n 個長度為 m 的完全相同的字元,就要比較所有的字元 S,S = n * m 。但對於正常情況,處於最短字串前的字串依舊要比較所有字元,而不是最短字串個字元,相對於解法一較差。
空間複雜度:O(1)。
解法三 遞迴
我們把原來的陣列分成兩部分,求出左半部分的最長公共字首,求出右半部分的最長公共字首,然後求出的兩個結果再求最長公共字首,就是最後的結果了。
求左半部分的最長公共字首,我們可以繼續把它分成兩部分,按照上邊的思路接著求。然後一直分成兩部分,遞迴下去。
直到該部分只有 1 個字串,那麼最長公共子串就是它本身了,直接返回就可以了。
public String longestCommonPrefix(String[] strs) {
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;
String lcpLeft = longestCommonPrefix(strs, l , mid);
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) )
return left.substring(0, i);
}
return left.substring(0, min);
}
複製程式碼
時間複雜度:
空間複雜度:
每次遇到遞迴的情況,總是有些理不清楚,先空著吧。
總
進行了垂直比較和水平比較,又用到了遞迴,solution 裡還介紹了二分查詢,感覺這裡用二分查詢有些太僵硬了,反而使得時間複雜度變高了。還介紹了字首樹,這裡後邊遇到再總結吧。