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

多模字符串匹配算法原理及Java實現(xiàn)代碼

 更新時間:2017年11月21日 11:26:24   作者:hwYang  
這篇文章主要介紹了多模字符串匹配算法原理及Java實現(xiàn)代碼,涉及算法背景,原理,構(gòu)建過程簡單介紹幾Java代碼實現(xiàn)等相關(guān)內(nèi)容,具有一定參考價值,需要的朋友可以了解下。

多模字符串匹配算法在這里指的是在一個字符串中尋找多個模式字符字串的問題。一般來說,給出一個長字符串和很多短模式字符串,如何最快最省的求出哪些模式字符串出現(xiàn)在長字符串中是我們所要思考的。該算法廣泛應(yīng)用于關(guān)鍵字過濾、入侵檢測、病毒檢測、分詞等等問題中。多模問題一般有Trie樹,AC算法,WM算法等等。

背景

在做實際工作中,最簡單也最常用的一種自然語言處理方法就是關(guān)鍵詞匹配,例如我們要對n條文本進行過濾,那本身是一個過濾詞表的,通常進行過濾的代碼如下

for (String document : documents) {
 for (String filterWord : filterWords) {
  if (document.contains(filterWord)) {
   //process ...
  }
 }
}

如果文本的數(shù)量是n,過濾詞的數(shù)量是k,那么復(fù)雜度為O(nk);如果關(guān)鍵詞的數(shù)量較多,那么支行效率是非常低的。

計算機科學(xué)中,Aho–Corasick算法是由AlfredV.Aho和MargaretJ.Corasick發(fā)明的字符串搜索算法,用于在輸入的一串字符串中匹配有限組“字典”中的子串。它與普通字符串匹配的不同點在于同時與所有字典串進行匹配。算法均攤情況下具有近似于線性的時間復(fù)雜度,約為字符串的長度加所有匹配的數(shù)量。然而由于需要找到所有匹配數(shù),如果每個子串互相匹配(如字典為a,aa,aaa,aaaa,輸入的字符串為aaaa),算法的時間復(fù)雜度會近似于匹配的二次函數(shù)。

原理

在一般的情況下,針對一個文本進行關(guān)鍵詞匹配,在匹配的過程中要與每個關(guān)鍵詞一一進行計算。也就是說,每與一個關(guān)鍵詞進行匹配,都要重新從文檔的開始到結(jié)束進行掃描。AC自動機的思想是,在開始時先通過詞表,對以下三種情況進行緩存:

按照字符轉(zhuǎn)移成功進行跳轉(zhuǎn)(success表)

按照字符轉(zhuǎn)移失敗進行跳轉(zhuǎn)(fail表)

匹配成功輸出表(output表)

因此在匹配的過程中,無需從新從文檔的開始進行匹配,而是通過緩存直接進行跳轉(zhuǎn),從而實現(xiàn)近似于線性的時間復(fù)雜度。

構(gòu)建

構(gòu)建的過程分三個步驟,分別對success表,fail表,output表進行構(gòu)建。其中output表在構(gòu)建sucess和fail表進行都進行了補充。fail表是一對一的,output表是一對多的。

按照字符轉(zhuǎn)移成功進行跳轉(zhuǎn)(success表)

sucess表實際就是一棵trie樹,構(gòu)建的方式和trie樹是一樣的,這里就不贅述。

按照字符轉(zhuǎn)移失敗進行跳轉(zhuǎn)(fail表)

設(shè)這個節(jié)點上的字母為C,沿著他父親的失敗指針走,直到走到一個節(jié)點,他的兒子中也有字母為C的節(jié)點。然后把當(dāng)前節(jié)點的失敗指針指向那個字母也為C的兒子。如果一直走到了root都沒找到,那就把失敗指針指向root。使用廣度優(yōu)先搜索BFS,層次遍歷節(jié)點來處理,每一個節(jié)點的失敗路徑。

匹配成功輸出表(output表)

匹配

舉例說明,按順序先后添加關(guān)鍵詞he,she,,his,hers。在匹配ushers過程中。先構(gòu)建三個表,如下圖,實線是sucess表,虛線是fail表,結(jié)點后的單詞是ourput表。

代碼

import java.util.*;
/**
 */
public class ACTrie {
	private Boolean failureStatesConstructed = false;
	//是否建立了failure表
	private Node root;
	//根結(jié)點
	public ACTrie() {
		this.root = new Node(true);
	}
	/**
  * 添加一個模式串
  * @param keyword
  */
	public void addKeyword(String keyword) {
		if (keyword == null || keyword.length() == 0) {
			return;
		}
		Node currentState = this.root;
		for (Character character : keyword.toCharArray()) {
			currentState = currentState.insert(character);
		}
		currentState.addEmit(keyword);
	}
	/**
  * 模式匹配
  *
  * @param text 待匹配的文本
  * @return 匹配到的模式串
  */
	public Collection<Emit> parseText(String text) {
		checkForConstructedFailureStates();
		Node currentState = this.root;
		List<Emit> collectedEmits = new ArrayList<>();
		for (int position = 0; position < text.length(); position++) {
			Character character = text.charAt(position);
			currentState = currentState.nextState(character);
			Collection<String> emits = currentState.emit();
			if (emits == null || emits.isEmpty()) {
				continue;
			}
			for (String emit : emits) {
				collectedEmits.add(new Emit(position - emit.length() + 1, position, emit));
			}
		}
		return collectedEmits;
	}
	/**
  * 檢查是否建立了failure表
  */
	private void checkForConstructedFailureStates() {
		if (!this.failureStatesConstructed) {
			constructFailureStates();
		}
	}
	/**
  * 建立failure表
  */
	private void constructFailureStates() {
		Queue<Node> queue = new LinkedList<>();
		// 第一步,將深度為1的節(jié)點的failure設(shè)為根節(jié)點
		//特殊處理:第二層要特殊處理,將這層中的節(jié)點的失敗路徑直接指向父節(jié)點(也就是根節(jié)點)。
		for (Node depthOneState : this.root.children()) {
			depthOneState.setFailure(this.root);
			queue.add(depthOneState);
		}
		this.failureStatesConstructed = true;
		// 第二步,為深度 > 1 的節(jié)點建立failure表,這是一個bfs 廣度優(yōu)先遍歷
		/**
   * 構(gòu)造失敗指針的過程概括起來就一句話:設(shè)這個節(jié)點上的字母為C,沿著他父親的失敗指針走,直到走到一個節(jié)點,他的兒子中也有字母為C的節(jié)點。
   * 然后把當(dāng)前節(jié)點的失敗指針指向那個字母也為C的兒子。如果一直走到了root都沒找到,那就把失敗指針指向root。
   * 使用廣度優(yōu)先搜索BFS,層次遍歷節(jié)點來處理,每一個節(jié)點的失敗路徑?! ?
   */
		while (!queue.isEmpty()) {
			Node parentNode = queue.poll();
			for (Character transition : parentNode.getTransitions()) {
				Node childNode = parentNode.find(transition);
				queue.add(childNode);
				Node failNode = parentNode.getFailure().nextState(transition);
				childNode.setFailure(failNode);
				childNode.addEmit(failNode.emit());
			}
		}
	}
	private static class Node{
		private Map<Character, Node> map;
		private List<String> emits;
		//輸出
		private Node failure;
		//失敗中轉(zhuǎn)
		private Boolean isRoot = false;
		//是否為根結(jié)點
		public Node(){
			map = new HashMap<>();
			emits = new ArrayList<>();
		}
		public Node(Boolean isRoot) {
			this();
			this.isRoot = isRoot;
		}
		public Node insert(Character character) {
			Node node = this.map.get(character);
			if (node == null) {
				node = new Node();
				map.put(character, node);
			}
			return node;
		}
		public void addEmit(String keyword) {
			emits.add(keyword);
		}
		public void addEmit(Collection<String> keywords) {
			emits.addAll(keywords);
		}
		/**
   * success跳轉(zhuǎn)
   * @param character
   * @return
   */
		public Node find(Character character) {
			return map.get(character);
		}
		/**
   * 跳轉(zhuǎn)到下一個狀態(tài)
   * @param transition 接受字符
   * @return 跳轉(zhuǎn)結(jié)果
   */
		private Node nextState(Character transition) {
			Node state = this.find(transition);
			// 先按success跳轉(zhuǎn)
			if (state != null) {
				return state;
			}
			//如果跳轉(zhuǎn)到根結(jié)點還是失敗,則返回根結(jié)點
			if (this.isRoot) {
				return this;
			}
			// 跳轉(zhuǎn)失敗的話,按failure跳轉(zhuǎn)
			return this.failure.nextState(transition);
		}
		public Collection<Node> children() {
			return this.map.values();
		}
		public void setFailure(Node node) {
			failure = node;
		}
		public Node getFailure() {
			return failure;
		}
		public Set<Character> getTransitions() {
			return map.keySet();
		}
		public Collection<String> emit() {
			return this.emits == null ? Collections.<String>emptyList() : this.emits;
		}
	}
	private static class Emit{
		private final String keyword;
		//匹配到的模式串
		private final int start;
		private final int end;
		/**
   * 構(gòu)造一個模式串匹配結(jié)果
   * @param start 起點
   * @param end  重點
   * @param keyword 模式串
   */
		public Emit(final int start, final int end, final String keyword) {
			this.start = start;
			this.end = end;
			this.keyword = keyword;
		}
		/**
   * 獲取對應(yīng)的模式串
   * @return 模式串
   */
		public String getKeyword() {
			return this.keyword;
		}
		@Override
		  public String toString() {
			return super.toString() + "=" + this.keyword;
		}
	}
	public static void main(String[] args) {
		ACTrie trie = new ACTrie();
		trie.addKeyword("hers");
		trie.addKeyword("his");
		trie.addKeyword("she");
		trie.addKeyword("he");
		Collection<Emit> emits = trie.parseText("ushers");
		for (Emit emit : emits) {
			System.out.println(emit.start + " " + emit.end + "\t" + emit.getKeyword());
		}
	}
}

總結(jié)

以上就是本文關(guān)于多模字符串匹配算法原理及Java實現(xiàn)代碼的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:

Java 蒙特卡洛算法求圓周率近似值實例詳解

java算法實現(xiàn)紅黑樹完整代碼示例

java實現(xiàn)的各種排序算法代碼示例

如有不足之處,歡迎留言指出。感謝朋友們對本站的支持。

相關(guān)文章

  • java后端如何實現(xiàn)防止接口重復(fù)提交

    java后端如何實現(xiàn)防止接口重復(fù)提交

    這篇文章主要介紹了java后端如何實現(xiàn)防止接口重復(fù)提交問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 深入理解Java中的volatile關(guān)鍵字(總結(jié)篇)

    深入理解Java中的volatile關(guān)鍵字(總結(jié)篇)

    volatile這個關(guān)鍵字,不僅僅在Java語言中有,在很多語言中都有的,而且其用法和語義也都是不盡相同的。這篇文章主要介紹了Java中的volatile關(guān)鍵字,需要的朋友可以參考下
    2018-10-10
  • Java實現(xiàn)簡單版貪吃蛇游戲

    Java實現(xiàn)簡單版貪吃蛇游戲

    這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)簡單版貪吃蛇游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Java基礎(chǔ)之練習(xí)打印三角形

    Java基礎(chǔ)之練習(xí)打印三角形

    這篇文章主要介紹了Java基礎(chǔ)之練習(xí)打印三角形,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • java實現(xiàn)CSV文件導(dǎo)入與導(dǎo)出功能

    java實現(xiàn)CSV文件導(dǎo)入與導(dǎo)出功能

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)CSV文件導(dǎo)入與導(dǎo)出,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Spring常用一些工具類實例匯總

    Spring常用一些工具類實例匯總

    這篇文章主要介紹了Spring常用一些工具類實例匯總,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-12-12
  • java8 計算時間差的方法示例

    java8 計算時間差的方法示例

    這篇文章主要介紹了java8 計算時間差的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • 淺談幾種Java自定義異常處理方式

    淺談幾種Java自定義異常處理方式

    在Java中,異常是一種常見的處理機制,本文主要介紹了淺談幾種Java自定義異常處理方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • SpringBoot整合PostgreSQL的示例代碼

    SpringBoot整合PostgreSQL的示例代碼

    本文主要介紹了SpringBoot整合PostgreSQL的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • 利用Java讀取Word表格中文本和圖片的方法實例

    利用Java讀取Word表格中文本和圖片的方法實例

    這篇文章主要給大家介紹了關(guān)于如何利用Java讀取Word表格中文本和圖片的相關(guān)資料,主要利用的是free spire.doc.jar 包,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2021-07-07

最新評論

禹城市| 佛山市| 从江县| 中西区| 伊宁县| 富川| 文登市| 白银市| 双峰县| 陇川县| 安龙县| 绩溪县| 三江| 陕西省| 宜宾市| 景宁| 武穴市| 乌拉特后旗| 惠水县| 南康市| 陇川县| 剑川县| 屏边| 富川| 佛学| 梅州市| 南川市| 宁陕县| 洞头县| 尼勒克县| 江西省| 开平市| 闽清县| 湘西| 江油市| 郧西县| 济源市| 邵阳县| 永修县| 漠河县| 东城区|