演算法 字串是否包含問題

2021-09-13 22:46:34 字數 4282 閱讀 1132

在網上看到這篇文章:一次谷歌面試趣事。覺得其中的演算法題以及作者的解決思路很有趣,就拿來分享一下吧。

問題假設這有乙個各種字母組成的字串,假設這還有另外乙個字串,而且這個字串裡的字母數相對少一些。從演算法是講,什麼方法能最快的查出所有小字串裡的字母在大字串裡都有?

比如,如果是下面兩個字串:

string 1: abcdefghlmnopqrs 

string 2: dcgsrqpo

答案是true,所有在string2裡的字母string1也都有。如果是下面兩個字串:

string 1: abcdefghlmnopqrs 

string 2: dcgsrqpz

答案是false,因為第二個字串裡的z字母不在第乙個字串裡。

解決方案

1. 輪詢

對於這種操作最簡單最幼稚的做法是輪詢第二個字串裡的每個字母,看它是否同在第乙個字串裡。從演算法上講,這需要o(n*m)次操作,其中n是string1的長度,m是string2的長度。就拿上面的例子來說,最壞的情況下將會有16*8 = 128次操作。

2. 排序

乙個稍微好一點的方案是先對這兩個字串的字母進行排序,然後同時對兩個字串依次輪詢。兩個字串的排序需要o(m log m) + o(n log n)次操作(常規情況下),之後的線性掃瞄需要o(m+n)次操作。同樣拿上面的字串做例子,將會需要16*4 + 8*3 = 88加上對兩個字串線性掃瞄的16 + 8 = 24的操作。(隨著字串長度的增長,你會發現這個演算法的效果會越來越好)

/*** 排序方案:快速排序

*/public static boolean issubsetbyquicksort(string a, string b)

if(c != ca[pos])

}return true;

}public static void quicksort(char arr, int low, int high)

arr[l] = arr[r];

while(l < r && arr[l] <= pivot)

arr[r] = arr[l];

}arr[l] = pivot;

quicksort(arr, low, l - 1);

quicksort(arr, l + 1, high);}1

2345

6789

1011

1213

1415

1617

1819

2021

2223

2425

2627

2829

3031

3233

3435

3637

3839

4041

4243

4445

46不過,常規排序比如快排可以達到o(n log n)的時間複雜度,這裡也可以選用用空間換時間的的基數排序、桶排序等線性時間複雜度的排序演算法。

// 字母編碼[a - z]:[65 - 122]

public static final int letter_region = 122 - 65 + 1;

/*** 排序方案:計數排序

*/public static boolean issubsetbycountersort(string a, string b)

if(c != ca[pos])

}return true;

}public static char countersort(char arr)

for(int i = 1; i < letter_region; i++)

char res = new char[arr.length];

for(char c : arr)

return res;}1

2345

6789

1011

1213

1415

1617

1819

2021

2223

2425

2627

2829

3031

3233

3435

3637

3839

4041

4243

4445

463. 雜湊表

雜湊表hashtable是乙個只需要o(n+m)次操作的演算法。方法就是,對第乙個字串進行輪詢,把其中的每個字母都放入乙個hashtable裡(時間成本是o(n),這裡是16次操作)。然後輪詢第二個字串,在hashtable裡查詢每個字母,看能否找到。如果找不到,說明沒有匹配成功。這將消耗掉8次操作 —— 這樣兩項操作加起來一共只有24次。不錯吧,比前面兩種方案都要好。

/*** 雜湊表hashset

*/public static boolean issubsetbyhashset(string a, string b)

for(char c : cb)

}return true;}1

2345

6789

1011

1213

1415

1617

184、bitmap位圖法

這個解決方案思想和hashtable一致,只不過使用的是位圖法來為每乙個字元保留一位。同樣只需要o(n+m)次操作。

// 字母編碼區間[a - z]:[65 - 122]

public static final int letter_region = 122 - 65 + 1;

/*** 位元位方案

*/public static boolean issubsetbybitmap(string a, string b)

for(char c : cb)

}return true;

}/**

* 寫入指定位的位元

*/public static void setbit(byte bitmap, int k)

/*** 讀取指定位的位元

*/public static int getbit(byte bitmap, int k)12

3456

78910

1112

1314

1516

1718

1920

2122

2324

2526

2728

2930

3132

3334

3536

37到此為止,o(n+m)幾乎是你能得到的最好的結果了,因為至少要對每個字母至少訪問一次才能完成這項操作,而上述這兩個方案是剛好是對每個字母只訪問一次。下面看看文章中最後的這個素數方案。

5. 素數

假設我們有乙個一定個數的字母組成字串。我給每個字母分配乙個素數,從2開始,往後類推。這樣a將會是2,b將會是3,c將會是5,等等。現在我遍歷第乙個字串,把每個字母代表的素數相乘。最終會得到乙個很大的整數,對吧?然後 —— 輪詢第二個字串,用每個字母除它。如果除的結果有餘數,這說明有不匹配的字母。如果整個過程中沒有餘數,你應該知道它是第乙個字串恰好的子集了。這樣不行嗎?

public static int primes =  ;

// 字母編碼區間[a - z]:[65 - 122]

public static final int letter_region = 122 - 65 + 1;

/*** 素數方案

*/public static boolean issubsetbyprimenumber(string a, string b)

system.out.println("乘積結果p = " + p.tostring());

for(char c : cb)

}return true;}1

2345

6789

1011

1213

1415

1617

1819

2021

2223

2425

2627

2829

3031

3233

34測試**

public class charactersubset }1

2345

6789

1011

1213

1415

1617

1819

2021

2223

2425

總結就如文章中所說,素數方案在演算法上並不能說就比雜湊表好。而且在實際操作中,你很可能仍會使用雜湊表的方案,因為它更通用,無需跟麻煩的大型數字打交道。但從」巧妙水平「上講,guy提供的素數方案是一種更、更、更有趣的方案。

字串是否包含問題

假設這有兩個分別由字母組成的字串a另外字串b,字串b的字母數較字串a少一些。什麼方法能最快地查出字串b所有字母是不是都在字串a裡?也就是說判斷字串b是不是字串a的真子集 為了簡化,姑且認為兩個集合都不是空集,即字串都不為空。解法一 暴力輪詢 就是將b中的每一字元都和a中的字元做對比,思想簡單此處就不...

字串是否包含問題

題目描述 假設這有乙個各種字母組成的字串a,和另外乙個字串b,字串裡b的字母數相對少一些。什麼方法能最快的查出所有小字串b裡的字母在大字串a裡都有?比如,如果是下面兩個字串 string 1 abcdefghlmnopqrs string 2 dcgsrqpo 答案是true,所有在string2裡...

字串包含問題演算法

現在假設有2個字串r和s,其中m r.len n s.len,設計乙個演算法判斷字串s中的每個字元在r串中均存在.顯然,很容易想到的乙個演算法,最粗魯最暴力演算法,其時間複雜度o m n 也就是對s字串中的每個字元在r中進行查詢判斷 這或許是我自己想到的最快的方法了。顯而易見,這樣的演算法或許不是演...