日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

字符串匹配算法(Trie树)

發布時間:2024/7/5 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字符串匹配算法(Trie树) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 1. Trie樹概念
    • 2. Trie樹操作
      • 2.1 存儲
      • 2.2 查找
      • 2.3 插入
      • 2.4 刪除
      • 2.5 打印
    • 3. 完整代碼
    • 4. Trie樹與散列表、紅黑樹的比較
      • 4.1 思考題
      • 參考文章
    • 5. 練習題

1. Trie樹概念

  • Trie樹,也叫字典樹,它是一個樹形結構。是一種專門處理字符串匹配的數據結構,用來解決在一組字符串集合中快速查找某個字符串。
  • Trie樹本質,利用字符串之間的公共前綴,將重復的前綴合并在一起。

2. Trie樹操作

2.1 存儲

Trie樹是一個多叉樹;二叉樹的數據結構里存放著左右子節點的指針;
Trie樹采用的一種經典的存儲方式是散列表。

class TrieNode//Trie樹節點類,假設只有26個字母的數據集 { public:char data;TrieNode *children[charNum];size_t count;//記錄這個節點被多少個單詞占用bool isEndOfWord;//是否是一個單詞的結束字符size_t freq; //單詞插入的頻次TrieNode(char ch = '/'):data(ch), isEndOfWord(false), count(0), freq(0){memset(children,0,sizeof(TrieNode*) * charNum);}~TrieNode(){} };

Trie樹比較浪費內存,children數組存放指針;
犧牲點效率的話,可以將數組改成,有序數組,跳表,散列表,紅黑樹等

2.2 查找

TrieNode* find_private(const string &text) const//查找某個字符串,返回最后一個字符節點的指針 {TrieNode *p = root;int index;for(int i = 0; i < text.size(); ++i){index = text[i] - 'a';if(p->children[index] == NULL)return NULL;//還沒匹配完p = p->children[index];}if(p->isEndOfWord == false)//匹配完,但是只是前綴return NULL;else{return p;//私有find無輸出信息} }

時間復雜度O(k),k為要查找的字符串長度

2.3 插入

void insert(const string &text)//插入一個字符串 {TrieNode *p = find_private(text);if(p)//找到了字符串,不用插入,頻次加1{p->freq++;return;}p = root;int index;for(int i = 0; i < text.size(); ++i){index = text[i] - 'a';if(p->children[index] == NULL){TrieNode *newNode = new TrieNode(text[i]);p->children[index] = newNode;}p->count++;p = p->children[index];}p->count++;p->freq++;p->isEndOfWord = true; }

時間復雜度O(n),n為所有字符串長度和

2.4 刪除

bool delString(const string &text) {TrieNode *p = root;stack<TrieNode*> nodeStack;nodeStack.push(root);int index;for(int i = 0; i < text.size(); ++i){index = text[i] - 'a';if(p->children[index] == NULL)return false;//還沒匹配完p = p->children[index];nodeStack.push(p);}if(p->isEndOfWord == false)//匹配完,但是只是前綴return false;else{while(nodeStack.top()->count == 1)//刪除單詞只要自己包含的部分{index = nodeStack.top()->data - 'a'; // cout << "del char: " << nodeStack.top()->data << endl;//(調試代碼)delete nodeStack.top();nodeStack.pop();}nodeStack.top()->children[index] = NULL;//斷開已刪除的部分while(!nodeStack.empty()){nodeStack.top()->count--;//單詞占用記錄減1nodeStack.pop();}return true;} }

析構函數

void destory(TrieNode* proot)//樹不再使用,結束前,釋放資源 {if(proot == NULL){return;}for(int i = 0; i < charNum; ++i){destory(proot->children[i]);}delete proot;proot = NULL; }

2.5 打印

void printStrWithPre(const string prefix) const//打印有指定前綴的單詞{if(prefix.size() == 0)return;TrieNode *p = root;int index,printID = 0;for(int i = 0; i < prefix.size(); ++i){index = prefix[i] - 'a';if(p->children[index] == NULL)//前綴還沒匹配成功{cout << "-------------------------" << endl;cout << "no string with prefix: " << prefix << " can be found!" << endl;return;}elsep = p->children[index];}//匹配完了,p指向前綴最后一個字符節點cout << "-------------------------" << endl;cout << p->count << " string(s) with prefix: " << prefix << " , as following:" << endl;printWordsOfNode(p,prefix,printID);cout << "-----------end-----------" << endl;}void printDict() const//字典序輸出全部單詞{string word("");int printID = 0;cout << "-------------------------" << endl;cout << "all " << itemCount() << " words as following:" << endl;printWordsOfNode(root,word,printID);cout << "-----------end-----------" << endl;} private:void printWordsOfNode(TrieNode* p, string prefix, int &order) const{//遞歸打印前綴最后一個字符對應節點下面所有的字符if(p != NULL){if(p->isEndOfWord)//是終止字符,prefix是不斷+出來的,是整個字符串cout << ++order << " " << prefix << ", frequency: " << p->freq << endl;for(int i = 0; i < charNum; ++i){if(p->children[i] != NULL)printWordsOfNode(p->children[i],prefix+(p->children[i]->data),order);}}}

3. 完整代碼

https://github.com/hitskyer/course/blob/master/dataAlgorithm/chenmingming/string_matching/trie.cpp

/*** @description: trie樹,字典樹* @author: michael ming* @date: 2019/6/24 19:00* @modified by: */ #include <iostream> #include <cstring> #include <stack> #define charNum 26 using namespace std; class TrieNode//Trie樹節點類,假設只有26個字母的數據集 { public:char data;TrieNode *children[charNum];size_t count;//記錄這個節點被多少個單詞占用bool isEndOfWord;//是否是一個單詞的結束字符size_t freq; //單詞插入的頻次TrieNode(char ch = '/'):data(ch), isEndOfWord(false), count(0), freq(0){memset(children,0,sizeof(TrieNode*) * charNum);}~TrieNode(){} }; class Trie { public:TrieNode* root;Trie(){root = new TrieNode;}~Trie(){destory(root);}void insert(const string &text)//插入一個字符串{TrieNode *p = find_private(text);if(p)//找到了字符串,不用插入,頻次加1{p->freq++;return;}p = root;int index;for(int i = 0; i < text.size(); ++i){index = text[i] - 'a';if(p->children[index] == NULL){TrieNode *newNode = new TrieNode(text[i]);p->children[index] = newNode;}p->count++;p = p->children[index];}p->count++;p->freq++;p->isEndOfWord = true;}void find(const string &text) const//查找某個字符串{TrieNode *p = root;int index;for(int i = 0; i < text.size(); ++i){index = text[i] - 'a';if(p->children[index] == NULL)//還沒匹配完{cout << "can not find string: " << text << endl;return;}p = p->children[index];}if(p->isEndOfWord == false)//匹配完,但是只是前綴{cout << "can not find string: " << text << endl;return;}else{cout << text << " occurs " << p->freq << " time(s)." << endl;return;}}private:TrieNode* find_private(const string &text) const//查找某個字符串,返回最后一個字符節點的指針{TrieNode *p = root;int index;for(int i = 0; i < text.size(); ++i){index = text[i] - 'a';if(p->children[index] == NULL)return NULL;//還沒匹配完p = p->children[index];}if(p->isEndOfWord == false)//匹配完,但是只是前綴return NULL;else{return p;//私有find無輸出信息}}public:void destory(TrieNode* proot)//樹不再使用,結束前,釋放資源{if(proot == NULL){return;}for(int i = 0; i < charNum; ++i){destory(proot->children[i]);}delete proot;proot = NULL;}bool delString(const string &text){TrieNode *p = root;stack<TrieNode*> nodeStack;nodeStack.push(root);int index;for(int i = 0; i < text.size(); ++i){index = text[i] - 'a';if(p->children[index] == NULL)return false;//還沒匹配完p = p->children[index];nodeStack.push(p);}if(p->isEndOfWord == false)//匹配完,但是只是前綴return false;else{while(nodeStack.top()->count == 1)//刪除單詞只要自己包含的部分{index = nodeStack.top()->data - 'a'; // cout << "del char: " << nodeStack.top()->data << endl;//(調試代碼)delete nodeStack.top();nodeStack.pop();}nodeStack.top()->children[index] = NULL;//斷開已刪除的部分while(!nodeStack.empty()){nodeStack.top()->count--;//單詞占用記錄減1nodeStack.pop();}return true;}}size_t itemCount() const//字典中單詞種數{return root->count;}void printStrWithPre(const string prefix) const//打印有指定前綴的單詞{if(prefix.size() == 0)return;TrieNode *p = root;int index,printID = 0;for(int i = 0; i < prefix.size(); ++i){index = prefix[i] - 'a';if(p->children[index] == NULL)//前綴還沒匹配成功{cout << "-------------------------" << endl;cout << "no string with prefix: " << prefix << " can be found!" << endl;return;}elsep = p->children[index];}//匹配完了,p指向前綴最后一個字符節點cout << "-------------------------" << endl;cout << p->count << " string(s) with prefix: " << prefix << " , as following:" << endl;printWordsOfNode(p,prefix,printID);cout << "-----------end-----------" << endl;}void printDict() const//字典序輸出全部單詞{string word("");int printID = 0;cout << "-------------------------" << endl;cout << "all " << itemCount() << " words as following:" << endl;printWordsOfNode(root,word,printID);cout << "-----------end-----------" << endl;} private:void printWordsOfNode(TrieNode* p, string prefix, int &order) const{//遞歸打印前綴最后一個字符對應節點下面所有的字符if(p != NULL){if(p->isEndOfWord)//是終止字符,prefix是不斷+出來的,是整個字符串cout << ++order << " " << prefix << ", frequency: " << p->freq << endl;for(int i = 0; i < charNum; ++i){if(p->children[i] != NULL)printWordsOfNode(p->children[i],prefix+(p->children[i]->data),order);}}} }; int main() {Trie textlib;string a("hello"), b("her"), c("so"), d("hi"), e("how"), f("see");textlib.insert(a);textlib.insert(a);textlib.insert(b);textlib.insert(c);textlib.insert(d);textlib.insert(e);textlib.insert(f);textlib.find(a);textlib.find(b);textlib.find(d);textlib.printStrWithPre("h");textlib.printDict();textlib.delString("hello");textlib.find(a);textlib.printStrWithPre("h");textlib.printDict();cout << "total kind(s) of word: " << textlib.itemCount() << endl;return 0; }

4. Trie樹與散列表、紅黑樹的比較

Trie樹對要處理的字符串有及其嚴苛的要求。

  • 第一,字符串中包含的字符集不能太大。如果字符集太大,那存儲空間可能就會浪費很多。即便可以優化,也要付出犧牲查詢、插入效率的代價。
  • 第二,要求字符串的前綴重合比較多,不然空間消耗會變大很多。
  • 第三,如果要用Trie樹解決問題,那我們就要自己從零開始實現一個Trie樹,還要保證沒有bug,這個在工程上是將簡單問題復雜化,除非必須,一般不建議這樣做。
  • 第四,通過指針串起來的數據塊是不連續的,而Trie樹中用到了指針,所以,對緩存并不友好,性能上會打個折扣。
  • 綜合這幾點,針對在一組字符串中查找字符串的問題,工程中更傾向于用散列表或者紅黑樹。因為這兩種數據結構,我們都不需要自己去實現,直接利用編程語言中提供的現成類庫就行了。
  • Trie 樹只是不適合精確匹配查找,這種問題更適合用散列表或者紅黑樹來解決。
  • Trie樹比較適合的是查找前綴匹配的字符串,例如搜索引擎智能匹配輸入,給出候選提示(如果有多個候選,可以按搜索熱度排序,上面代碼里面的 frequency)。
  • Trie樹還可以應用于自動輸入補全(輸入法,代碼編輯器,瀏覽器網址輸入)

4.1 思考題

  • 上面針對英文的搜索關鍵詞,對于更加復雜的中文來說,詞庫中的數據又該如何構建成Trie 樹呢?
  • 如果詞庫中有很多關鍵詞,在搜索提示的時候,用戶輸入關鍵詞,作為前綴在Trie 樹中可以匹配的關鍵詞也有很多,如何選擇展示哪些內容呢?(按搜索熱度或者概率)
  • 像Google 這樣的搜索引擎,用戶單詞拼寫錯誤的情況下,Google還是可以使用正確的拼寫來做關鍵詞提示,這個又是怎么做到的呢?

參考文章

https://www.cnblogs.com/xujian2014/p/5614724.html

5. 練習題

LeetCode 1707. 與數組中元素的最大異或值(Trie樹)

總結

以上是生活随笔為你收集整理的字符串匹配算法(Trie树)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 又黄又色又爽的视频 | 按摩害羞主妇中文字幕 | 精品少妇一区二区三区免费观 | 中出白浆 | 精品在线91 | 日本不卡在线 | 黑人乱码一区二区三区av | 在线观看免费视频 | 中日精品一色哟哟 | 黄色污污视频 | 久久成人精品视频 | 麻豆 美女 丝袜 人妻 中文 | 国产农村妇女精品一区二区 | 成人av影院在线观看 | 成人午夜免费在线观看 | 婷婷影院在线观看 | 肉色欧美久久久久久久免费看 | 欧美一级在线看 | 午夜影院在线 | 日本高清视频www | 精品国模一区二区三区 | 日韩资源站 | 少妇黄色一级片 | 丝袜制服中文字幕 | 亚洲精品免费在线观看视频 | re久久| 她也啪在线视频 | 解开乳罩喂领导吃奶 | 97色伦影院 | 闺蜜张开腿让我爽了一夜 | 欧美拍拍视频 | 亚洲色图国产视频 | 尤物视频在线观看国产 | 麻豆成人在线 | 99精品视频在线播放免费 | 国产一区二区视频在线观看免费 | av无码精品一区二区三区 | 神马一区二区三区 | 啦啦啦免费高清视频在线观看 | 97人妻人人澡人人爽人人精品 | 性色av一区二区三区免费 | 香蕉大人久久国产成人av | 国产无遮挡一区二区三区毛片日本 | 欧美黑人添添高潮a片www | 国产成人a v | 欧美综合在线一区 | 亚洲天堂一区在线 | 欧美日韩视频免费 | 爱福利视频网 | 高跟鞋肉丝交足91 | 热久久久久久久 | xnxx国产| 中文字幕激情小说 | 99视频热| jizz国产 | 久艹伊人 | 国产一区二区三区久久久 | 中文字幕欧美人妻精品一区蜜臀 | 日韩黄色免费视频 | 又黄又高潮的视频 | 无法忍受在线观看 | 成年人视频免费在线观看 | 肉肉av福利一精品导航 | 久久久久久久久久久久Av | 亚洲五月综合 | 国产精品suv一区二区88 | 孕妇疯狂做爰xxxⅹ 国产精品乱码久久久久久 99久久久成人国产精品 | 夜夜爽av福利精品导航 | 国产男男gay体育生白袜 | 国产特黄级aaaaa片免 | 日本在线播放视频 | 久草午夜 | 双性受孕h堵精大肚生子 | 国产suv精品一区二区四 | 欧美亚洲综合久久 | 婷婷在线视频观看 | 婷婷精品进入 | 亚洲精选av | 国产情侣在线播放 | 亚洲综合色网 | 精品亚洲一区二区三区四区五区 | 操比网站 | 亚洲色欧美 | 久久香蕉综合 | 欧美国产一二三区 | 精品久久久亚洲 | 亚洲无码一区二区三区 | 日本福利在线观看 | 久久久国产精华液 | 国模无码视频一区二区三区 | 深夜视频一区二区 | 中文字幕高清av | 青青草原国产视频 | 高h免费视频 | 国产喷水吹潮视频www | 中文字幕在线看片 | 国产精品久久久久久亚洲毛片 | 九月婷婷色 | 中日韩精品视频在线观看 |