二分查找的妙用:判定子序列

2021-03-03 算法與數據結構

二分查找本身不難理解,難在巧妙地運用二分查找技巧。對於一個問題,你可能都很難想到它跟二分查找有關,比如前文 最長遞增子序列就藉助一個紙牌遊戲衍生出二分查找解法。

今天再講一道巧用二分查找的算法問題:如何判定字符串s是否是字符串t的子序列(可以假定s長度比較小,且t的長度非常大)。舉兩個例子:

s = "abc", t = "ahbgdc", return true.

s = "axc", t = "ahbgdc", return false.

題目很容易理解,而且看起來很簡單,但很難想到這個問題跟二分查找有關吧?

一、問題分析

首先,一個很簡單的解法是這樣的:

bool isSubsequence(string s, string t) {
    int i = 0, j = 0;
    while (i < s.size() && j < t.size()) {
        if (s[i] == t[j]) i++;
        j++;
    }
    return i == s.size();
}

其思路也非常簡單,利用雙指針i, j分別指向s, t,一邊前進一邊匹配子序列:

讀者也許會問,這不就是最優解法了嗎,時間複雜度只需 O(N),N 為t的長度。

是的,如果僅僅是這個問題,這個解法就夠好了,不過這個問題還有 follow up

如果給你一系列字符串s1,s2,...和字符串t,你需要判定每個串s是否是t的子序列(可以假定s相對較短,t很長)。

boolean[] isSubsequence(String[] sn, String t);

你也許會問,這不是很簡單嗎,還是剛才的邏輯,加個 for 循環不就行了?

可以,但是此解法處理每個s時間複雜度仍然是 O(N),而如果巧妙運用二分查找,可以將時間複雜度降低,大約是 O(MlogN),M 為 s的長度。由於 N 相對 M 大很多,所以後者效率會更高。

二、二分思路

二分思路主要是對t進行預處理,用一個字典index將每個字符出現的索引位置按順序存儲下來(對於 ASCII 字符,可以用大小為 256 的數組充當字典):

比如對於這個情況,匹配了 "ab",應該匹配 "c" 了:

按照之前的解法,我們需要j線性前進掃描字符 "c"。但現在藉助index中記錄的信息,可以二分搜索index[c]中比 j 大的那個索引,在上圖的例子中,就是在[0,2,6]中搜索比 4 大的那個索引:

這樣就可以快速得到下一個 "c" 的索引 6。現在的問題就是,如何用二分查找計算那個恰好比 4 大的索引呢?答案是,尋找左側邊界的二分搜索就可以做到。

三、再談二分查找

在前文 二分查找算法詳解中,詳解了如何正確寫出三種二分查找算法的細節。二分查找返回目標值val的索引,對於搜索左側邊界的二分查找,有一個特殊性質:

當val不存在時,得到的索引恰好是比val大的最小元素索引

什麼意思呢,就是說如果在數組[0,1,3,4]中搜索元素 2,算法會返回索引 2,也就是元素 3 的位置,元素 3 就是數組中大於 2 的最小元素。所以我們可以利用二分搜索避免線性掃描。

以上就是搜索左側邊界的二分查找,等會兒會用到,其中的細節可以參見 二分查找算法詳解,這裡不再贅述。

四、代碼實現

這裡以處理單個字符串s為例,對於多個字符串s,把預處理部分單獨抽出來即可。

算法執行的過程是這樣的:

可見藉助二分查找,算法的效率是可以大幅提升的:預處理時需要 O(N) 時間,每次匹配子序列的時間是 O(MlogN),比之前每次匹配都要 O(N) 的時間要高效得多。

當然,如果只需要判斷一個 s 是否是 t 的子序列,是不需要二分查找的,一開始的 O(N) 解法就是最好的,因為雖然二分查找解法處理每個 s 只需要 O(MlogN),但是還需要 O(N) 時間構造 index 字典預處理,所以處理單個s時沒有必要。

以上就是二分查找技巧判定子序列的全部內容,希望你能有所收穫。

●編號1041,輸入編號直達本文

●輸入m獲取文章目錄

程式設計師數學學習

鍛鍊數學邏輯思維

相關焦點

  • 雖然你看過,但就是寫不出來系列-最長上升(下降)子序列並輸出子序列
    任何疑問、意見、建議請公眾號留言或聯繫qq474356284    給定一個無序的整數數組,找到其中最長上升子序列的長度。
  • 二分查找和大O表示法
    這種查找方式是----二分查找。二分查找的方法如下:        ①猜想的數字是0-100,那麼先從0-100的中間數 50 開始,則會有如下結論:50雖然小了,但是我們排除了0-50的數字。代碼實現:       編寫執行二分查找的python代碼,代碼示例使用數組(在python中叫做列表)        tips:1、給出的序列必須有序,才可以進行二分查找                 2、在python3中,/表示浮點數除法,//表示整數除法
  • 二分查找算法及其應用
    二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。
  • 二分查找和插值查找
    二分查找就是:在一組數據中取出中間的那個元素,將其和要找的元素對比,如果不是要找的元素,則以其為標記點,將這組數據分成兩半,取出符合條件的一半遞歸進行如上操作直到找出數據為止。二分查找的複雜度為O(logN),在數據量小的情況下這兩種查找算法的效率相差無幾,但是在數據量很大的情況下,二分查找的性能就遠遠高於順序查找了。
  • 二分查找
    二分查找又稱折半查找(Binary Search),是一種效率較高的查找方法。
  • 二分查找算法案列詳解
    1、前言最近刷了很多二分查找相關的題目,這裡將近期的收穫做一個總結,包括二分查找的變形問題。如果能掌握,我相信以後基本上二分查找相關的問題對你來說,都不是問題。2、二分查找的效率二分查找是啥我想不用過多的說明。
  • 算法入門——二分查找法
    二分查找法      假設我們要在一個電話簿中查找一個名字為g名字開頭的人,我們可以從頭開始翻頁
  • 二分查找,沒那麼簡單!
    今天討論,二分查找二分查找注意事項二分查找表面看似很簡單,就是對解空間的不斷二分,直至逼近解並返回。但是實操起來,卻絕非這麼簡單。要首先分析出對誰折半,有些問題顯而易見,如有序數組查某個數的位置,因為索引值越大,元素值就越大,所以對索引範圍折半。
  • Python 的二分查找法,聽說你還不知道是啥?
    點擊上方藍字,關注:無量測試之道    作者 | 無量測試之道   編輯 | 小 晴今天分享的主題內容是:二分查找法。Python 中與除法相關的三個運算符是// 和 / 和 %:「/」,這是傳統的除法,5/2=2.5「//」,在Python 中,這個叫「地板除」,9//4=2「%」,這個是取模操作,也就是取餘數,4%2=0,5%2=11、什麼是二分查找法    二分查找也稱折半查找
  • 算法總結:最長上升子序列(LIS)
    最長上升子序列,英文名 Longest Increasing Subsequence,簡稱 LIS,在面試中屬於難度較大,但又非常高頻的題目。最長遞增子序列(https://leetcode-cn.com/problems/longest-increasing-subsequence/)總體思路如果你不知道最長上升子序列是什麼意思,也沒有做那道題,我——當然不會怪你 😊最長上升(遞增)子序列這道題是最最基本的,也是最常考的題了,其他題目都是由它衍生而來。
  • Leetcode刷題-二分查找
    本文對部分涉及二分查找算法的leetcode題目進行了學習與實踐,並給出了個人的二分查找python模板二分查找算法解釋二分查找算法(英語:binary search algorithm),也稱折半搜索算法(英語:half-interval search algorithm
  • 聊聊一看就會一寫就跪的二分查找
    要說哪個算法的知名度較高,二分查找一定排得上號。後來我又在維基百科上發現了這樣一段話:儘管二分查找的基本思想相對簡單,但細節可以令人難以招架 —— 高德納當喬恩·本特利將二分查找問題布置給專業編程課的學生時,90%的學生在花費數小時後還是無法給出正確的解答,主要因為這些錯誤程序在面對邊界值的時候無法運行,或返回錯誤結果。1988年開展的一項研究顯示,20本教科書裡只有5本正確實現了二分查找。
  • 經典動態規劃問題:最長公共/遞增子序列
    [1,3,5]是一個子序列,但卻不是一個子數組,因為子數組要求元素連續。[3,1,5]是一個子集,但既不是一個子序列,也不是一個子數組。因為後兩者都要元素順序不能改變。給定兩個數組A和B,找出兩者共同擁有的子序列中長度最長的一個。通常只需要返回長度即可。
  • 啟動子序列提取-EPD真核生物啟動子資料庫!
    基因結構有很多概念,經常會把我們繞暈,之前小編給大家整理過mRNA、CDS、ORF等概念知識(感興趣的點擊查看:CDS、cDNA、ORF等等傻傻分不清);今天我們再補充一些概念性知識及著重說下啟動子以及啟動子序列查找方法
  • 使用Python和C語言實現二分法查找(折半查找)
    二分查找 叫折半查找,它對要查找的序列有兩個要求,一是該序列必須是有序的,二是該序列必須是順序存儲的。二分法查找算法的原理如下:1、 如果待查對象為空返回-1,表示查找不到目標元素2、 如果待查序列不為空,則將中間元素與要查找的目標元素進行匹配看是否相等,如果相等表示 查找成功,返回該中間元素的索引如果不相等再比較這兩個元素的大小
  • 如何使用 NCBI 查找基因序列、mRNA、Promoter | 實驗
    主要有以下四部分內容:今天我們就先講第一部分:如何查找基因序列、mRNA、Promoter。這裡主要用到的是 Map viewer,我們以人的 IL6(白細胞介素 6)為例講述一下具體的操作步驟。1.下面參考序列給出了三個,是不同的部門做出來的。經我驗證,序列有微小的差異,但總體來說基本相同。儘管你分別點擊後,序列代碼等有所差異,但鹼基基本一致,不影響大家研究分析序列。現在普遍採用的是最上面的那個序列,這一條是世界範圍的生物科學家用計算機合成的 一個序列。我也推薦大家使用這個序列。4.
  • 一文帶你從代碼的角度詳細搞懂什麼是二分查找樹
    一、二分查找二分查找應該應熟悉了吧?要在順序儲存結構中進行查找,跟最中間的數據進行比較,小的話去前半部分進行查找,否則,去後半部分去查找,其實,可以用迭代和遞歸分別來實現二分查找。1、迭代法  首先,用迭代法來實現,代碼如下:// 二分查找法,在有序數組arr中,查找target// 如果找到target,返回相應的索引index// 如果沒有找到target,返回-1
  • excel函數技巧:妙用「=」進行查找替換函數功能
    妙用1:查找替換實現數據平均分組比如下圖的這個問題,要把30個人分成6組,每組5個人,高手可以用公式來完成:=OFFSET($A$1,ROW(A1)+COLUMN(A1)*5-5,)公式用到了數列的構造(關於數列構造的原理
  • excel替換技巧:妙用「=」進行查找替換函數功能 續
    前面為大家分享了妙用「=」進行查找替換的文章,今天咱們繼續。雖然下面分享的這兩條妙用在網上也能查到,但絕大部分查到的都沒有本教程操作乾淨、利索。妙用4:按顏色求和但不用定義名稱工作中經常用顏色來標定一些符合某種條件的數據。現在標定好了,怎麼按顏色求和呢?