最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語言實(shí)現(xiàn)字符串搜索算法Boyer-Moore

 更新時(shí)間:2023年11月15日 17:01:41   作者:YanChen11  
Boyer-Moore?算法是一種非常高效的字符串搜索算法,被廣泛的應(yīng)用于多種字符串搜索場景,下面我們就來學(xué)習(xí)一下如何利用Go語言實(shí)現(xiàn)這一字符串搜索算法吧

Boyer-Moore 算法是一種非常高效的字符串搜索算法,被廣泛的應(yīng)用于多種字符串搜索場景:

  • 文本搜索(尤其是大篇幅的文本搜索)
  • 文檔編輯器以及 IDE 工具中的字符串搜索/替換
  • 編譯器中搜索源代碼中的關(guān)鍵字/符號
  • 文件系統(tǒng)中搜索給定的文件名

通常,在字符串搜索過程中,我們期望盡快得到結(jié)果(提高算法運(yùn)行速度,降低時(shí)間復(fù)雜度),為此我們需要對字符串(文本以及搜索的子串)進(jìn)行一些預(yù)處理。對于大文本,該預(yù)處理過程會消耗可觀的內(nèi)存空間,而如果在搜索過程中該預(yù)處理過程需要反復(fù)進(jìn)行,則又會消耗相當(dāng)?shù)?CPU 資源。

Boyer-Moore 算法只需要對被搜索的子串進(jìn)行一次預(yù)處理,通過在本次預(yù)處理過程中收集到的信息來提升算法的運(yùn)行效率,使得算法的時(shí)間復(fù)雜度盡量趨近于 O(n)。Boyer-Moore 算法的一個(gè)顯著特征是匹配過程從子串的末尾開始向前,如果遇到不匹配的字符,則根據(jù)在預(yù)處理過程中收集到的信息進(jìn)行跳躍/移動,避免逐個(gè)字符進(jìn)行比較。而具體的跳躍/移動規(guī)則,則同時(shí)使用兩種策略實(shí)現(xiàn):

  • 壞字符啟發(fā)
  • 好后綴啟發(fā)

⒈ 壞字符啟發(fā)

在將子串中的字符與文本中的字符進(jìn)行比較時(shí),如果文本中的字符與子串中的字符不匹配,我們稱文本中的當(dāng)前字符為壞字符。對于壞字符,通常有兩種處理方式:

壞字符在子串中的其他位置存在

當(dāng)壞字符在子串中存在時(shí),如果壞字符在子串中的位置位于當(dāng)前索引位置之前,則將子串中的壞字符與文本中的壞字符對齊,然后重新從子串的最后開始向前與文本中的字符進(jìn)行匹配;如果壞字符在子串中的位置位于當(dāng)前索引位置之后,則將子串向后移動一個(gè)字符的位置,然后重新開始從子串的最后開始向前與文本中的字符進(jìn)行匹配。

如上圖所示,從后往前將子串中的字符與文本中的字符進(jìn)行比較,子串中的字符 C 與文本中的 R 不匹配。但文本中的字符 R 在子串中的其他位置存在,此時(shí)將子串中的 R 與文本中的 R 對齊,然后重新從后往前進(jìn)行匹配。

在對子串進(jìn)行預(yù)處理時(shí),由于子串中 R 出現(xiàn)了兩次,所以實(shí)際預(yù)處理完成之后收集到的信息中記錄的是位于 C 之后的 R 的位置信息。所以,實(shí)際在代碼中,遇到這種情況,子串只能向后移動一個(gè)字符的位置。

壞字符在子串中不存在

如果壞字符在子串中不存在,那么移動子串,將子串的第一個(gè)字符與文本中壞字符之后的字符對齊,然后重新從后往前將子串中的字符與文本中的字符進(jìn)行匹配。

如上圖所示,壞字符 G 與子串中的字符 Q 不匹配,并且壞字符 G 在子串中并不存在,此時(shí)將子串移動到與壞字符 G 后的字符對齊,然后重新從后往前開始匹配。

package main

import (
	"fmt"
)

func main() {
	text := "AYRRQMGRPCRQ"
	subStr := "RPCRQ"
	fmt.Printf("text = %+v\n", text)
	fmt.Printf("subStr = %+v\n", subStr)

	// 構(gòu)建子字符串中各個(gè)字符及相應(yīng)的索引的映射
	m := make(map[byte]int, len(subStr))
	for i := 0; i < len(subStr); i ++ {
		m[subStr[i]] = i
	}
	fmt.Printf("m = %+v\n", m)

	shiftLength := 0
	subIndex := len(subStr) - 1
	for shiftLength <= len(text) - len(subStr) {
		// 每次比較都從子字符串的末尾開始向前,逐個(gè)字符進(jìn)行比較
		for subIndex >= 0 && text[shiftLength + subIndex] == subStr[subIndex] {
			subIndex --
		}

		if subIndex == -1 {
			// 子字符串在文本中出現(xiàn),跳過文本中的子字符串繼續(xù)向后查找
			fmt.Printf("subStr found in text at index %+v\n", shiftLength)
			shiftLength += len(subStr)
		} else if v, ok := m[text[shiftLength + subIndex]]; ok {
			// 文本中的字符與子字符串中相應(yīng)位置的字符不匹配,但該字符在子字符串中存在
			if subIndex > v {
				// 如果該字符在子字符串中的位置在當(dāng)前索引位置之前,則將二者位置對齊,然后重新查找
				shiftLength += subIndex - v
			} else {
				// 文本中的索引位置向前移動一個(gè)字符(考慮子字符串中同一個(gè)字符重復(fù)出現(xiàn)的情況)
				shiftLength += 1
			}
		} else {
			// 文本中的字符在子字符串中不存在
			shiftLength += subIndex
		}

		subIndex = len(subStr) - 1
	}
}

⒉ 好后綴啟發(fā)

在將子串按照從后往前的順序與文本中的字符進(jìn)行比較時(shí),遇到不匹配的字符時(shí),子串末尾已經(jīng)匹配得字符即為好后綴。此時(shí)根據(jù)好后綴在子串中其他位置是否存在,重新確定子串在文本中開始匹配的位置。

好后綴或好后綴的后綴在子串中的其他位置存在

如上圖所示,從后往前將子串中的字符與文本進(jìn)行匹配,文本中字符 Y 與子串中相應(yīng)位置的字符 Q 不匹配,此時(shí)出現(xiàn)好后綴 CRQ。雖然好后綴 CRQ 在子串中只出現(xiàn)了一次,但好后綴的后綴 RQ 卻在子串的頭部再次出現(xiàn),此時(shí)將子串頭部的 RQ 與文本中的 RQ 對齊,然后重新從后往前匹配。

好后綴在子串中不存在

如上圖所示,子串中不存在好后綴,此時(shí)只要將文本中的起始匹配位置向后移動一個(gè)字符,然后重新從后往前匹配。

起始匹配位置移動之后,子串中出現(xiàn)了好后綴 RQ,并且 RQ 在子串的頭部也存在。此時(shí),將子串頭部的 RQ 與文本中的 RQ 對齊,確定新的匹配開始位置,重新匹配。

好后綴啟發(fā)的關(guān)鍵在于對子串的預(yù)處理。

在對子串進(jìn)行預(yù)處理的過程中,需要確定子串中單個(gè)字符以及多個(gè)連續(xù)字符出現(xiàn)的頻次以及相應(yīng)的位置。當(dāng)匹配過程中出現(xiàn)好后綴時(shí),會根據(jù)預(yù)處理過程中收集到的信息確定文本中新的開始匹配的位置。

package main

import (
	"fmt"
)

func main() {
	text := "AYCRQMGRQCRQ"
	subStr := "RQCRQ"
	fmt.Printf("text = %+v\n", text)
	fmt.Printf("subStr = %+v\n", subStr)

	shifts := make([]int, len(subStr) + 1)
	borderPosition := make([]int, len(subStr) + 1)

	// 確定搜索字符串中各個(gè)子串的邊界
	i, j := len(subStr), len(subStr) + 1
	borderPosition[i] = j

	for i > 0 {
		for j >= 0 && j <= len(subStr) && subStr[i - 1] != subStr[j - 1] {
			if shifts[j] == 0 {
				shifts[j] = j - i
			}

			j = borderPosition[j]
		}

		i --
		j --
		borderPosition[i] = j
	}

	fmt.Printf("shifts = %+v\n", shifts)
	fmt.Printf("borderPosition = %+v\n", borderPosition)

	// 確定搜索字符串中各個(gè)字符與文本中的字符不匹配時(shí)應(yīng)該移動的距離
	j = borderPosition[0]
	for i := 0; i <= len(subStr); i ++ {
		if shifts[i] == 0 {
			shifts[i] = j
		}

		if i == j {
			j = borderPosition[j]
		}
	}

	fmt.Printf("shifts = %+v\n", shifts)
	fmt.Printf("borderPosition = %+v\n", borderPosition)

	// 在文本中搜索字符串
	shiftLength := 0
	for shiftLength <= len(text) - len(subStr) {
		j = len(subStr) - 1
		for j >= 0 && subStr[j] == text[shiftLength + j] {
			j --
		}

		if j == -1 {
			fmt.Printf("subStr find in text at position %+v\n", shiftLength)
			shiftLength += shifts[0]
		} else {
			shiftLength += shifts[j + 1]
		}
	}
}

以上就是Go語言實(shí)現(xiàn)字符串搜索算法Boyer-Moore的詳細(xì)內(nèi)容,更多關(guān)于Go字符串搜索算法的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語言RPC Authorization進(jìn)行簡單ip安全驗(yàn)證的方法

    Go語言RPC Authorization進(jìn)行簡單ip安全驗(yàn)證的方法

    這篇文章主要介紹了Go語言RPC Authorization進(jìn)行簡單ip安全驗(yàn)證的方法,實(shí)例分析了Go語言進(jìn)行ip驗(yàn)證的技巧,需要的朋友可以參考下
    2015-03-03
  • 深入了解Go語言中database/sql是如何設(shè)計(jì)的

    深入了解Go語言中database/sql是如何設(shè)計(jì)的

    在?Go?語言中內(nèi)置了?database/sql?包,它只對外暴露了一套統(tǒng)一的編程接口,便可以操作不同數(shù)據(jù)庫,那么database/sql?是如何設(shè)計(jì)的呢,下面就來和大家簡單聊聊吧
    2023-07-07
  • Golang中urlencode與urldecode編碼解碼詳解

    Golang中urlencode與urldecode編碼解碼詳解

    這篇文章主要給大家介紹了關(guān)于Golang中urlencode與urldecode編碼解碼的相關(guān)資料,在Go語言中轉(zhuǎn)碼操作非常方便,可以使用內(nèi)置的encoding包來快速完成轉(zhuǎn)碼操作,Go語言中的encoding包提供了許多常用的編碼解碼方式,需要的朋友可以參考下
    2023-09-09
  • Golang中堆排序的實(shí)現(xiàn)

    Golang中堆排序的實(shí)現(xiàn)

    堆是一棵基于數(shù)組實(shí)現(xiàn)的特殊的完全二叉樹,本文主要介紹了Golang中堆排序的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Go語言中的Slice學(xué)習(xí)總結(jié)

    Go語言中的Slice學(xué)習(xí)總結(jié)

    這篇文章主要介紹了Go語言中的Slice學(xué)習(xí)總結(jié),本文講解了Slice的定義、Slice的長度和容量、Slice是引用類型、Slice引用傳遞發(fā)生“意外”等內(nèi)容,需要的朋友可以參考下
    2014-11-11
  • Go?slice切片make生成append追加copy復(fù)制示例

    Go?slice切片make生成append追加copy復(fù)制示例

    這篇文章主要為大家介紹了Go使用make生成切片、使用append追加切片元素、使用copy復(fù)制切片使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • golang引入自定義包的兩種方法

    golang引入自定義包的兩種方法

    本文主要介紹了golang引入自定義包的兩種方法,第一種是傳統(tǒng)的手動管理,第二種是使用go.mod文件,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-03-03
  • 詳解golang避免循環(huán)import問題(“import cycle not allowed”)

    詳解golang避免循環(huán)import問題(“import cycle not allowed”)

    這篇文章主要給大家介紹了關(guān)于golang中不允許循環(huán)import問題("import cycle not allowed")的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • 詳解Golang如何優(yōu)雅接入多個(gè)遠(yuǎn)程配置中心

    詳解Golang如何優(yōu)雅接入多個(gè)遠(yuǎn)程配置中心

    這篇文章主要為大家為大家介紹了Golang如何優(yōu)雅接入多個(gè)遠(yuǎn)程配置中心詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • 詳解Go strconv包

    詳解Go strconv包

    Go語言中strconv包實(shí)現(xiàn)了基本數(shù)據(jù)類型和其字符串表示的相互轉(zhuǎn)換。這篇文章主要介紹了Go strconv包的相關(guān)知識,需要的朋友可以參考下
    2020-10-10

最新評論

大姚县| 南溪县| 邵武市| 呼和浩特市| 于田县| 桃源县| 泰兴市| 互助| 温宿县| 辛集市| 肃北| 山阴县| 太原市| 宁蒗| 浦城县| 哈巴河县| 当涂县| 金门县| 红安县| 进贤县| 三河市| 黑水县| 林西县| 广昌县| 织金县| 岢岚县| 临朐县| 新邵县| 枝江市| 宁陵县| 贵州省| 台中市| 宁河县| 临夏市| 南雄市| 思南县| 思茅市| 裕民县| 屏东县| 温泉县| 马公市|