演算法系列之二十一 最短摘要的生成

2021-06-28 19:19:58 字數 4219 閱讀 5102

題目描述

分析與解法

這個問題比較完整正規的說明是:

給定一段產品的英文描述,包含m個英文本母,每個英文單詞以空格分隔,無其他標點符號;再給定n個英文單詞關鍵字,請說明思路並程式設計實現方法

string extractsummary(string description,string key words)
簡單分析如下:

@owen:掃瞄過程始終保持乙個[left,right]的range,初始化確保[left,right]的range裡包含所有關鍵字則停止。然後每次迭代:

1.試圖右移動left,停止條件為再移動將導致無法包含所有關鍵字。

2.比較當前range's length和best length,更新最優值。

3.右移right,停止條件為使任意乙個關鍵字的計數+1。

4.重複迭代。

更進一步,我們可以對問題進行如下的簡化:

假設給定的已經是經過網頁分詞之後的結果,詞語序列陣列為w。其中w[0], w[1],…, w[n]為一些已經分好的詞語。

那麼,我們該怎麼做呢?

解法一

「程式設計師面試、演算法研究、程式設計藝術、紅黑樹4大經典原創系列集錦與總結

出處:

那麼,我們可以猜想一下可能的分詞結果:

--/結構/之/法/演算法/之/道/blog/之/博主/....「(網頁的分詞效果w陣列)

這也就是我們期望的w陣列序列。

之前的q陣列序列為:

「結構之法」(使用者輸入的關鍵字q陣列)
再看下下面這個w-q序列:

w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1
我們可以進一步可以想象,如下:

從搜尋引擎的角度看:搜尋引擎經過把上述網頁分詞後,便得到了上述的分詞效果,然後在這些分詞中查詢「結構之法」4個關鍵字,但這4個關鍵字不一定只會出現一遍,它可能會在這篇文章中出現多次,就如上面的w-q序列一般。咱們可以假想出下面的結果(結構之法便出現了兩次):

由此,我們可以得出解決此問題的思路,如下:

依次操作下去,一直到遍歷至目標陣列w的最後乙個位置為止。

最終,通過比較,咱們確定如下分詞序列作為最短摘要,即搜尋引擎給出的分詞效果:

那麼,這個演算法的時間複雜度如何呢?

解法二

我們試著降低此問題的複雜度。因為上述思路一再進行查詢的時候,總是重複地迴圈,效率不高。那麼怎麼簡化呢?先來看看這些序列:

w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1
w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1
那麼,下次掃瞄應該怎麼辦呢?先把第乙個被掃瞄的位置挪到q0處。

w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1
w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1
//july、updated,2011.10.21

int ntargetlen = n + 1; // 設定目標長度為總長度+1

int pbegin = 0; // 初始指標

int pend = 0; // 結束指標

int nlen = n; // 目標陣列的長度為n

int nabstractbegin = 0; // 目標摘要的起始位址

int nabstractend = 0; // 目標摘要的結束位址

while(true)

while(isallexisted())

pbegin++;

} if(pend >= n)

break;

}

小結:上述思路二相比於思路一,很明顯提高了不小效率。我們在匹配的過程中利用了可以省去其中某些死板的步驟,這讓我想到了kmp演算法的匹配過程。同樣是經過觀察,比較,最後總結歸納出的高效演算法。我想,一定還有更好的辦法,只是我們目前還沒有看到,想到,待我們去發現,創造。

解法三

關於最短摘要的生成,我覺得july的處理有些簡單,我以july的想法為基礎,提出了自己的一些想法,這個問題分以下幾步解決:

1.將傳入的key words生成雜湊表,便於以後的字串比較。結構為keyhash,如下:

struct keyhash

結構體中的hash代表了關鍵字的雜湊值,key代表了關鍵字,cnt代表了在當前的掃瞄過程中,掃瞄到的該關鍵字的個數。

當然,作為雜湊表結構,該結構體中還會有其它值,這裡不贅述。

初始狀態下,所有雜湊結構的cnt欄位為0。

2.建立乙個keyword結構,結構體如下:

struct keyword

key欄位指向了建立的乙個keyword代表了當前掃瞄到的乙個關鍵字,掃瞄到的多個關鍵字組成乙個雙向鍊錶。

start欄位指向了關鍵字在文章中的起始位置。

3.建立幾個全域性變數:

keyword* head,指向了雙向鍊錶的頭,初始為null。

keyword* tail,指向了雙向鍊錶的尾,初始為null。

int minlen,當前掃瞄到的最短的摘要的長度,初始為0。

int minstartpos,當前掃瞄到的最短摘要的起始位置。

int needkeycnt,還需要幾個關鍵字才能夠包括全部的關鍵字,初始為關鍵字的個數。

4.開始對文章進行掃瞄。每掃瞄到乙個關鍵字時,就建立乙個keyword的結構並且將其連入到掃瞄到的雙向鍊錶中,更新head和tail結構,同時將對應的keyhash結構中的cnt加1,表示掃瞄到了關鍵字。如果cnt由0變成了1,表示掃瞄到乙個新的關鍵字,因此needkeycnt減1。

5.當needkeycnt變成0時,表示掃瞄到了全部的關鍵字了。此時要進行乙個操作:煉表頭優化。煉表頭指向的word是摘要的起始點,可是如果對應的keyhash結構中的cnt大於1,表示掃瞄到的摘要中還有該關鍵字,因此可以跳過該關鍵字。因此,此時將煉表頭更新為下乙個關鍵字,同時,將對應的keyhash中的結構中的cnt減1,重複這樣的檢查,直至某個煉表頭對應的keyhash結構中的cnt為1,此時該結構不能夠少了。

6.如果找到更短的minlength,則更新minlength和minstartpos。

7.開始新一輪的搜尋。此時摘除鍊錶的第乙個節點,將needkeycnt加1,將下乙個節點作為煉表頭,同樣的開始煉表頭優化措施。搜尋從上一次的搜尋結束處開始,不用回溯。就是所,搜尋在整個演算法的過程中是一直沿著文章向下的,不會回溯,直至文章搜尋完畢。

這樣的演算法的複雜度初步估計是o(m+n)。

8.另外,我覺得該問題不具備實際意義,要具備實際意義,摘要應該包含完整的句子,所以摘要的起始和結束點應該以句號作為分隔。

這裡,新建立乙個結構:sentence,結構體如下:

struct sentence

掃瞄到的多個句子結構組成乙個鍊錶。增加兩個全域性變數,分別指向了sentence鍊錶的頭和尾。

掃瞄時,建立關鍵字鍊錶時,也要建立sentence鍊錶。當掃瞄到包含了所有的關鍵字時,必須要掃瞄到乙個完整句子的結束。開始做sentence頭節點優化。做法是:檢視sentence結構中的全部key結構,如果全部的key對應的keyhash結構的cnt屬性全部大於1,表明該句子是多餘的,去掉它,去掉它的時候更新對應的hashkey結構的關鍵字,因為減去了很多的關鍵字。然後對下乙個sentence結構做同樣的操作,直至某個sentence結構是必不可少的,就是說它包含了當前的摘要中只出現過一次的關鍵字!

掃瞄到了乙個摘要後,在開始新的掃瞄。更新sentence鍊錶的頭結點為下乙個節點,同時更新對應的keyhash結構中的cnt關鍵字,當某個cnt變成0時,就遞增needkeycnt變數。再次掃瞄時仍然是從當前的結束位置開始掃瞄。

初步估計時間也是o(m+n)。

機器學習經典演算法之(二十一) 嶺回歸

一 嶺回歸簡介 線性回歸最主要問題是對異常值敏感。在真實世界的資料收集過程中,經過會遇到錯誤的度量結果。而線性回歸使用的普通最小二乘法,其目標是使平方誤差最小化。這時,由於異常值誤差的絕對值很大,因此破壞整個模型。如何解決呢?我們引入正則化項的係數作為閾值來消除異常的影響。這個方法稱為嶺回歸。具體原...

RHEL6入門系列之二十一,管理交換分割槽

換分割槽的概念在前面曾提到過,交換分割槽類似於windows系統中的虛擬記憶體,能夠在一定程度上緩解物理記憶體不足的問題。當物理記憶體接近不足時,系統會將記憶體中不經常呼叫 cpu不經常處理的資料轉移到交換分割槽中,以騰出更多的記憶體空間讓常用的程式使用。在windows系統中是採用乙個名為page...

MySQL學習筆記之二十一 使用者的許可權

一 資料庫和表的許可權 下列許可權運用於資料庫和表上的操作。select 允許你使用select語句從表中檢索資料。select語句只有在他們真正從乙個表中檢索行是才需要select許可權,你可以執行某個select語句,甚至沒有任何到伺服器上的資料 庫里的訪問任何東西的許可。例如,你可使用mysq...