日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

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

编程问答

suffix tree

發布時間:2025/4/16 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 suffix tree 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章出處:http://www.cnblogs.com/snowberg/archive/2011/10/21/2468588.html

3???What is a Suffix Tree

Suffix tree, 或?后綴樹?,是一種相當神奇的數據結構,它包含了字符串中的大量信息,能夠用于解決很多復雜的字符串問題 —— 事實上,基本上目前為止俺遇到過的所有與字符串有關的問題都可以通過它解決。

我們以字符串?MISSISSIPPI?為例,先列出它所有的后綴:

1. MISSISSIPPI 2. ISSISSIPPI 3. SSISSIPPI 4. SISSIPPI 5. ISSIPPI 6. SSIPPI 7. SIPPI 8. IPPI 9. PPI A. PI B. I

將它們分別插入到?字典樹?中我們可以得到:

BTW, 樹中的?$?符號表示字符串結尾 —— 這里?I?是字符串?MISSISSIPPI?的結尾,而?I?又在其中間出現了,所以有必要使用一個特殊的符號標識出來,同理在后綴樹中也需要有這樣的一個符號;雖然 C 語言中有'\0'?,但這里使用?$?符號似乎屬于約定俗成的事情了,咱這次也就從了它吧。

這樣的一棵字典樹已經具備了后綴樹的功能 ——

比如說吧,你從根節點開始順著這個字典樹的隨便一個樹枝走 n 步,然后數一下再往下有幾片葉子,就知道已經走過的這 n 個字符組成的字符串在原字符串中一共出現過多少次了 —— 由于你可以把某一節點下方的葉節點數目保存在該節點中,所以這個算法實際上是 O(n) 的,怎么樣?

只是,你也看出來了,它的空間復雜度相當之高,實際上基本上沒有誰會用這樣一個數據結構。

于是,?把所有最近兩個有多個分支的節點之間的部分(它們每個節點各自僅有一個分支)合并起來?(參考:Patricia Trie?或曰?Radix Tree),我們可以得到:

這就是一棵后綴樹了。

順便提一句,雖然邏輯結構如上,但實際的數據結構中存儲的東西大致是這樣的:

這樣,每一條邊占用的空間為常數大小,于是后綴樹 Tree(T) 的空間復雜度為 O(|T|),|T| 為字符串 T 的長度。?因為?:

  • 從樹根到樹葉的每一條路徑組成了一個后綴
  • 所以樹葉的數量等于后綴的數量
  • 每個非葉子節點都有至少兩條樹枝
  • 所以非葉節點的數量小于葉節點的數量
  • 除去根節點,節點數等于邊數
  • 所以,后綴樹的空間復雜度為: O(|T| + size(edge labels))?[1]

后綴樹和?后綴 Trie?一樣包含了整個字符串中所有子串的信息,又僅占用 O(|T|) 空間,而且 —— 后面會說到,它可以在 O(|T|) 時間內構造出來,這些特性使它成為了一個理論分析以及實際應用上都非常重要的數據結構。

4???What Can It Do

后綴樹的用途是如此之廣以至于 Dan Gusfield 在他的書中?[2]?花了七十多頁來介紹,我在這里先挑幾個自己看來比較好玩的例子說說,以期待激發讀者的興趣。

4.1???Longest Common Substring

Longest common substring?- 即?最長公共子串?,是指在兩個字符串中都出現了的最長子串 —— 比如,superiorcalifornialives?和?sealiver?的最長公共子串為?alive?。

這是個很經典的例子,據說 1970 年,后綴樹的概念還沒有被提出來的時候,Knuth 爺爺曾無比確信找出兩個字符串最長公共子串的線性算法是不存在的?[2]?;然而,在知道后綴樹的概念之后,這個算法也顯而易見了:

?

  • 將兩個字符串 T1, T2 加入到同一個后綴樹,并在節點上標記該節點屬于哪個字符串。
  • 遍歷后綴樹(該后綴樹中節點數目不大于 2(|T1|+|T2|),請參照后綴樹空間復雜度的證明 ),找出最深的、同時屬于兩個字符串的節點。
  • 從根節點到這個結點的路徑就是它們的最長公共子串了。
  • 4.2???Exact String Matching

  • 當字符串 T 已知且固定不變,P 改變時:

    我們可以用 O(|T|) 時間預處理 T,然后在 O(|P|) 時間內回答出 “P 在 T 中出現了幾次?” 或是 “P 在 T 的哪些地方出現過?” 之類的問題:

    預處理 T, 得到它的后綴樹 —— 也可以把多個字符串 T1, T2 ... 放入同一個后綴樹,然后查找 P 時只需要從這個后綴樹的根開始順著 P 往下走,然后讀出保存在節點中的信息 (下方的樹葉數量、出現地方 &etc.) 就行了。

    而這也就意味著你可以把很大的文檔進行?索引?,然后快速的進行搜索。

  • 當字符串 P 已知且固定不變時:

    我們也可以用 O(|P|) 時間預處理模式串 P,然后用 O(|T|) 時間在 T 中找出 P —— 時間、空間復雜度的上界就和 KMP 算法或是 Boyer-Moore 算法一樣。

    這個可能看不明顯,但是,這么說吧,KMP 算法中 Next 數組保存的所有信息、Boyer-Moore 算法中 Bad-Charactor 和 Good-Suffix 表的信息后綴樹中都有。 由于實際操作上比較復雜,這里就不展開了,讀者請自行參考?[2]?或者耐心等待?If?(嗯,也就是本人) 心血來潮的那一天。

  • 當 P 和 T 都已知的時候:

    很明顯,我們可以在 O(|T| + |P|) 時間內找出 P 在 T 中的出現,不說了。

  • 4.3???Ziv-Lempel Compression

    嗯,常說的 LZ77 指的就是 Lempel Ziv 1977。

    Ziv-Lempel 壓縮的主要思想是,將前方出現過的子串使用?(位置, 長度) 對?表示出來,例如aaaaaaaaaaaaaaaa?就可以表示為?a(1,1)(1,2)(1,4)(1,8)?:

    a(1,1) == aa aa(1,2) == aaaa aaaa(1,4) == aaaaaaaa aaaaaaaa(1,8) == aaaaaaaaaaaaaaaa

    真正的 LZ 編碼方式和這里說的有點區別,但思想是一樣的。

    有了后綴樹,LZ 壓縮也簡單了:

  • 對要壓縮的字符串?T?構造出后綴樹?Tree(T)?。
  • 遍歷這棵樹,在每個節點?v?上標出該節點下方的所有樹葉中(位置)最小的值?C(v)?。
  • 當需要知道位置?i?上的?(位置, 長度)?編碼時:
  • 順著字符串?T[ i..m ]?的前綴走到?p?點。
  • 設?v?是?p?下方的第一個節點(或者?p?本身,如果?p?是節點的話)。
  • 設?d?是?p?的深度。
  • 找出滿足?d?+?C(v)?<=?i?,?d?盡可能大的?p?點,則此時從根節點到?p?的路徑就是最長的、同時出現在了?T[ 1..i ]?中的?T[ i..m ]?的前綴。
  • 即,?T[ C(v)..C(v)+d ]?==?T[ i..i+d ]?。
  • 于是,?T[ i..i+d ]?就可以表示成?(C(v), d)?序列了。
  • 以上查找 (位置, 長度) 序列的算法時間復雜度是 O(長度),于是壓縮算法整體復雜度就是 O(|T|) 。

    —— BTW,后面會說到,根據 Ukkonen 的算法?[3]?,后綴樹本身可以 "On-line" 構造,即,每次加入一個字符,得到當前的后綴樹;這樣, Ziv-Lempel 壓縮也可以僅用一次自左向右的掃描完成。

    5???Few New Concepts

    在繼續講如何構造后綴樹之前,為了后面表述的方便,再提幾個簡單的概念:

    Explicit Node (顯式節點)?:
    出現在后綴樹里了的所有節點都可以稱為?顯示節點?。
    Internal Node (內部節點)?:
    為了和葉節點區分開來,我們給不是葉節點的顯式節點起個新名字,叫做?內部節點?。
    Implicit Node (隱式節點)?:
    出現在了?后綴 Trie?中卻沒有出現在后綴樹中的那些節點是?隱式節點?。
    顯式擴展?:
    對后綴樹做了點什么
    隱式擴展?:
    對后綴樹實際上啥都沒做

    6???How To Build It

    目前我只弄懂了 Ukkonen 的算法?[3]?,所以下面也只說這一種算法:

    6.1???Native Approach

    首先,我們觀察發現,若?S?是一個非空字符串,?c?是一個字符,則將?c?加到?S?的每個后綴(包括空后綴)后面,我們可以得到?Sc?的所有后綴。

    Suffixes(a):aSuffixes(ab):abbSuffixes(abc):abcbccSuffixes(abca):abcabcacaa...

    很顯然,可以將這樣的原理用于構建后綴樹:假設 T[0..i-1] 的后綴樹已經建好了,那么在 T[0..i-1] 的每個后綴 T[0..i-1], T[1..i-1] .. T[j..i-1] .. T[i-1..i-1] 的后面加上字符 T[i] 就可以得到 T[0..i] 的后綴樹了。

    具體到 “將 T[i] 加到后綴 T[j..i-1]” 的這個過程,則有可能遇到三種情況:

  • 在后綴樹中,T[j..i-1] 停在了樹葉上。
    這種情況下,只需要把 T[i] 加到該樹葉的邊上即可。
  • 在后綴樹中,T[j..i-1] 的后面緊跟著的字符的集合 {a,b,c ... } 中不包含 T[i]。
    這種情況下,在 T[j..i-1] 的后面加上樹葉 T[i]
    (可能需要把隱式節點變換成顯式節點)。
  • 后綴樹中已經有 T[j..i] 了。
    這種情況下,我們什么都不用做。
  • 于是,如果不加任何形式的優化,那么 Ukkonen 的算法就是這么回事:

    令 n = |T|; 在 T 尾部加上終結符 $。 for i from 0 to n: // n+1 個階段(包括加上終結符)for j from 0 to i-1: // 每個階段 i 次擴展從后綴樹的根順著 T[ j..i-1 ] 走到節點 v (顯式/隱式)如果 v 下方節點中存在第一個字符為 T[i] 的,則什么都不用做。否則如果 v 是隱式節點,那么:將 v 改成顯式節點。在 v 下增加葉節點,從 v 到新節點的邊標記為 T[i]。若 v 是內部節點或根節點:在 v 下增加葉節點,從 v 到新節點的邊標記為 T[i]。否則: (只能是葉節點了):將 T[i] 加到邊的尾部。

    具體的,這里給出構建?MISSISSIPPI$?的后綴樹的全過程,如下:

    6.2???Speed Up!

    上面的原生態算法雖通俗易懂老少皆宜,但其時間復雜度相當之高 ( O(|T|3) ), 想要有實用價值必須得提高它的效率 —— 而正如前面提到過的,其實后綴樹可以在 O(|T|) 時間內構造;這個 O(|T|) 的奇妙算法和上面的算法主體流程其實一樣,下面就要介紹加速妙方了,請坐穩。

    6.2.1???Stop When?T[j..i]?Exists

    首先說最容易理解的:

    很顯然,如果后綴樹 STree(T[0..i-1]) 中已經存在 T[j..i] 了,那么 T[j+1 .. i] 肯定也在其中,?因為?:

  • T[0..i-1] 的所有后綴都在后綴樹中。
  • T[0..i-1] 的任意子串都可以表示為它的某一個后綴的前綴。
  • 所以 T[0..i-1] 的所有子串都在后綴樹中。
  • T[j+1 .. i] 是 T[j..i] 的子串, T[j..i] 又是 T[0..i-1] 的子串,所以 T[j+1 .. i] 也是 T[0..i-1] 的子串。
  • 所以后綴樹中存在 T[j+1 .. i] 。
  • 于是乎,碰到 T[j..i] 存在的情況,就不必繼續傻乎乎的在樹中查找 T[j+1 .. i-1], T[j+2 .. i-1] ... 了。

    6.2.2???Opened Leaves

    若知道了字符串的長度?l?,又知道了樹葉的起點?d?,就可以知道這個樹葉的邊的長度為?l?-?d?—— 所以首先,不記錄樹葉的邊長并不影響功能。

    而如果直接把所有的樹葉都標記為開放的,做法例如令其長度為無窮大 —— 這樣有一個好處:在對樹葉的擴展中就什么都不用做了。

    什么時候可以利用這個小技巧呢?我們看看將 T[i] 加入到 STree(T[0 .. i-1]) 的過程:

    • 首先, T[0 .. i-1] 肯定停在樹葉上。

    • 假設 STree(T[0 .. i-1]) 有 n 片樹葉,那么 T[1 .. i-1] T[2 .. i-1], T[n-1 .. i-1] 也都會停在樹葉上,因為:

      對于任何 j < i-1 如果 T[j .. i-1] 不在樹葉上,那么 T[j+1 .. i-1] 更不可能在樹葉上;

      這又是因為, T[j+1 .. i-1] 是 T[j .. i-1] 的后綴,若 T[0 .. i-1] 有子串 S = T[j .. i-1]+'c' (因此 T[j .. i-1] 不在樹葉上),那么 T[0 .. i-1] 肯定也有子串 S[1:] = T[j+1 .. i-1]+'c' (因此 T[j+1 .. i-1] 也不在樹葉上)。

      于是如果 STree(T[0 .. i-1]) 有 n 片樹葉,那一定就對應著前 n 個后綴。

    所以,已經有了 STree(T[0 .. i-1]) 的話,不必沿著 T[0 .. i-1], T[1 .. i-1] .. T[i-1 .. i-1] 所有這些路徑將 T[i] 加入到后綴樹;而只需要從 n 開始沿著路徑 T[n .. i-1], T[n+1 .. i-1] .. T[i-1 .. i-1] 加入 T[i] 。

    6.2.3???Count & Skip

    再看從后綴樹的根開始查找路徑 T[j .. i-1] 的過程,不難發現:

    某一條邊上進行匹配時,不必一個字符一個字符的比較,而只需要比較第一個字符,跳過剩下來的部分、直接去下一個節點,因為:

  • 一個節點每個分支的第一個字符都必定不相等(如果它們相等,則應該被放在上方的邊上)。
  • 另一方面,在對 STree(T[0 .. i-1]) 進行擴展,即將 T[i] 加到 T[0 .. i-1] 的每一個后綴后時, T[j .. i-1] (0 <= j <= i-1) 已經在樹中了,因此順著 T[j .. i-1] 查找下一次擴展的位置時,不可能遇到某一段邊實際不匹配但第一個字符相等的情況。
  • 6.2.4???Suffix Link

    好了,接下來要講最重要也是最難理解的話題:后綴鏈接了。

    6.2.4.1???What is It

    用漢語解釋 Suffix link 的話,它就是某種顯式節點之間的鏈接,其目的是簡化順著 T[j .. i-1] 找到下一次擴展的地方的過程。

    根據?Count & Skip?的技巧,從根節點開始找到擴展點的算法復雜度已經只與下方的分支數有關了,如果能直接從某個很接近擴展點的顯式節點開始查找,那么顯然能夠提高效率。

    很巧的,我們發現,如果有某個內部節點的路徑為?aS,其中?a?是一個字符、?S?是一個(可能為空的)字符串,那么肯定也會有一個路徑為?S?的內部節點、即使沒有,也會在下次擴展的時候產生。

    因為: 如果?aS?停在一個內部節點上面,也就是?aS?后有分支,那么當前的 T[0 .. i-1] 肯定有子串?aS+c 以及aS+d ( c 和 d 是不同的兩個字符) 這兩個不同的子串,于是肯定也有?S+c 以及?S+d 兩個子串了。至于“下次擴展時產生”的這種情況,則發生在已經有?aS+c 、?S+c ,剛加入?aS+d (于是產生了新的內部節點),正要加入?S+d 的時候。

    這樣,我們將節點?aS?指向?S?,并把這樣的一個指向關系稱為?aS?的?后綴鏈接?。

    見圖中紅色箭頭:

    6.2.4.2???How It Works

    后綴鏈接有什么用呢?

    前面似乎提示過了,在?aS?后加上了 d 之后,下一個步驟便是在?S?之后加上 d ;于是同樣,在?aSxy?后加上 z (步驟一)之后,下一個步驟是在?Sxy?后加上 z (步驟二)。如果?aS?停在了內部節點上,那么?S?也停在內部節點上,那么從步驟一到步驟二,通過后綴鏈接,可以直接找到路徑為?S?的這個內部節點;然后通過Count & Skip?技巧,可以直接定位到?xy?后方的擴展點。

    參考圖中?A?節點到?B?節點的藍色路徑:

    這里用?MISSISSIPPI?的例子來講后綴鏈接似乎不太合適,參考它的構造過程圖示,第九步之前樹中一直只有一個內部節點,第九步沒有利用后綴鏈接加速,而第九步之后,根據?Opened Leaves?技巧,只需要對?P?開頭的那一條邊進行擴展……但這個例子絕對不意味著后綴鏈接沒有用。

    嗯,比如說,作為一個正面的案例,構建?MISSISSIPPIMISSIA?的后綴樹的第 17 階段(加入?'A'?),第 13、14、15 次擴展便是順著后綴鏈接進行的(?ISSI?->?SSI?->?SI?->?I?->?ROOT?)。

    6.2.4.3???How To Maintain It

    那么如何建立和維護后綴鏈接呢?

    依然根據上面提到的原理,我們發現:

    在某一階段中,前一次擴展訪問到(或者建立)的內部節點到本次擴展訪問到的地方要么已經有了一條后綴鏈接,要么就應該建立一條后綴鏈接。

    因此,具體實現起來,我們可以保存一個最后訪問的內部節點的指針?p?,訪問到新的內部節點?node?的時候,將?p?的后綴鏈接指向?node?,再將?p?設為?node?即可。

    6.3???Why O(n)

    至于為什么這個算法是線性的,分析起來倒是不難:

    設 n = |T|。

  • 首先,對于每個階段的隱式擴展,花費的都是常數時間,因此整體上是 O(n)。
  • 然后,每次顯式擴展都會產生新的樹葉,而整個后綴樹中有 n 個樹葉;因此顯式擴展一共會執行 n 次。 ([2]?中說是 2n?次,為什么?)
  • 通過使用?Suffix Link?+?Count & Skip?技巧,在所有這 n 次顯式擴展中,總共的節點訪問次數為 O(n) 。這又是因為:
    • 假設節點 A 的后綴鏈接指向節點 B ,A 的路徑為?pa?, B 的路徑為?pb?, A 上方有?x?個節點, B 上方有?y?個節點,則有?:
    • x?<=?y?+ 1 (因為?pb?是?pa?的后綴,因此除了第一個字符之后的分支,?pa?路徑上的其他分支?pb路徑上必然也有,反之則不然)。
    • 因此,再看每次顯式擴展的過程:從當前位置向下找到擴展點;完成擴展; 向上一個節點;跟隨后綴鏈接來到下一個位置。其中除了“從當前位置向下找到擴展點”這一步之外,剩下的都只需要常數時間。
    • 而“向下”查找一次之后,跟隨后綴鏈接來到新的一個節點時最多只能使上方的節點數減少 2。
    • 后綴樹整體的深度不超過?n?,而總共又只有?n?次顯式擴展,因此節點的訪問次數為 O(n)
  • 因此,顯式擴展整體的時間復雜度也是 O(n)。
  • 于是,算法的時間復雜度便是 O(n) 了。
  • 6.4???C++ Code

    最后,以下代碼是我的實現,使用 boost license 以及 GPL v3 發布,需要用的話請自由選擇~

    使用模板一方面是因為字符的確有不同的類型,另一方面(更主要的)是方便把實現和定義放在同一個文件里,以減少鏈接的工作(我懶嘛),用 C 的童鞋,對不住了。

    本人對下面程序的任何 bug 引起的任何后果不負任何責任,但是如果發現有 bug 請您務必與俺聯系。

    /** File: suffixtree.h* Author: If* Created on Sep/14th/2010, 14:02** License: GPL v3 / Boost Software License 1.0*//**update: Oct/17th/2010:Record belongTo information on leaves only,other works were left to `dfs`, `bfs` and `eachLeaf`function.*update: Oct/17th/2010:Add `eachLeaf` function, for it's quite useful*//* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following:The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *//* Copyright (C) 2010 IfThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. *//* following code may cause permanent brain damage, read at yourown risk. */#ifndef SUFFIXTREE_H #define SUFFIXTREE_H// #define HAS_BOOST#include <functional> #include <algorithm> #include <stdexcept> #include <iterator> #include <iostream> #include <vector> #include <string> #include <stack> #include <queue>#include <assert.h> #include <stdio.h>#ifdef HAS_BOOST #include <boost/pool/object_pool.hpp> #endifusing std::vector; using std::basic_string;#ifdef DEBUG_STREE FILE* __streeLogFile=stderr; #define dbg(...) \ do{ \ fprintf(__streeLogFile, __VA_ARGS__); \ fflush(__streeLogFile); \ } while(0) #define dbg_puts(str, len) \ do{ \ for(int i=0;i<len;++i) \ fputc(str[i], __streeLogFile); \ fflush(__streeLogFile); \ } while(0) #else #define dbg(...) /*nothing at all*/ #define dbg_puts(s, l) /*nothing at all*/ #endiftemplate <class CharT, class Additional=void*> struct Node {// Important members:CharT const* str; // since edge-node has one-to-one correspondenceint len; // there is no need to separate them.int startAt; // for the use of presenting an implicit node.// it will then be the position of first different// charactor.Node * link; // suffix link.Node * parent;vector<Node*> branches;int belongTo; // which string does this leaf belongs to.// *only recorded on leaves*// Misc members:int leafNum; // leaf number ( nth suffix ).int depth; // no need to explain.Additional info; // some more information you need, depending on// your application.Node():str(0),len(0),startAt(0),link(0),parent(0),branches(),belongTo(-1),leafNum(0),depth(0),info(){ }Node(Node const& other):str(other.str),len(other.len),startAt(other.startAt),link(other.link),parent(other.parent),branches(other.branches),belongTo(other.belongTo),leafNum(other.leafNum),depth(other.depth),info(other.info){ }~Node() { }/*static inline int matchFirst(Node const& node, CharT const val){return node.str[0]==val? node.len : 0;}static inline bool ptrMatchFirst(Node const* node, CharT const val){return node->str[0]==val? node->len : 0;}*/struct MatchFirst {CharT val;MatchFirst(CharT v):val(v){ }inline bool operator()(Node const* node) const {return node->str[0]==val;}}; };template <class CharT=char, class T=void*> class SuffixTree { protected:typedef Node<CharT,T> NodeT;typedef typename vector<NodeT*>::iteratorNodeIterator; #ifdef HAS_BOOSTboost::object_pool<NodeT>allNodes_; #endifNodeT* root_;vector<NodeT*> leaves_;vector<NodeT*> allLeaves_;int nLeaves_;vector<basic_string<CharT> >strings_;NodeT* latestInternalNode_;NodeT* currentPos_;int phase_;int extension_;protected:NodeT* allocNode() { #ifdef HAS_BOOSTreturn allNodes_.malloc(); #elsereturn new NodeT; #endif}void freeNode(NodeT *& node) { #ifdef HAS_BOOSTallNodes_.free(node); #elsedelete node; #endifnode = 0;}/*** following str[0:len], and then add str[len] to current tree.** @return true if undone, false if str[0..len] already exists - which means* extension process is done.*/bool extend (CharT const* str, int pos);// follow path "str" from node down.NodeT* jumpDown(CharT const* str, int len, NodeT* node, bool fromFront=0);/*** split one edge from its startAt position.** @param edge: a node with startAt!=0* @param leafLabel: label of the newly created leaf, with length==1* @return newly created leaf.*/NodeT* splitEdge(NodeT* edge, CharT const* leafLabel);void putEnd() {int p = phase_+1;int const nth = strings_.size()-1;for(NodeIterator itr=leaves_.begin(),end=leaves_.end(); itr!=end; ++itr){(*itr)->len = --p - (*itr)->depth;allLeaves_.push_back(*itr);}}#ifndef HAS_BOOSTinline void destroy(NodeT* node) {for(NodeIterator itr=node->branches.begin(), end=node->branches.end();itr!=end; ++itr)destroy(*itr);freeNode(node);} #endifprivate:SuffixTree(SuffixTree<CharT,T> const&); // complicated while useless,// i've got no interest.public:inline SuffixTree();inline SuffixTree(basic_string<CharT> const&);#ifdef HAS_BOOST~SuffixTree() { /*nothing to do*/ } #else~SuffixTree() { destroy(root_); } #endifvoid addString(CharT const * str, int len);void addString(basic_string<CharT> const& str) {addString(str.c_str(), str.length()); // to maintain a copy}NodeT const* root() {return root_;} /*void deleteString(CharT const * str, int len);void deleteString(basic_string<CharT> const& str) {deleteString(str.c_str(), str.length());} */// will call f(node) on each node of this tree, including root.template <class Function>void dfs(Function f) {std::stack<NodeT*> todo;todo.push(root_);while(!todo.empty()) {NodeT* node = todo.top();todo.pop();for(NodeIterator itr=node->branches.begin(),end=node->branches.end();itr!=end;++itr) {todo.push(*itr);}f(*node);}}// will call f(node) on each node of this tree, including root.template <class Function>void bfs(Function f) {std::queue<NodeT*> todo;todo.push(root_);while(!todo.empty()) {NodeT* node = todo.front();todo.pop();for(NodeIterator itr=node->branches.begin(),end=node->branches.end();itr!=end;++itr) {todo.push(*itr);}f(*node);}}template <class Function>void eachLeaf(Function f) {for(NodeIterator itr=allLeaves_.begin(), end=allLeaves_.end();itr!=end; ++itr) {f(**itr);}}// human-readable json-like format outputvoid print(std::basic_ostream<CharT> * stream);// machine-readable dot format output, can be used to// generate graph using Graphvizvoid dot(std::basic_ostream<CharT> * stream); };template <class CharT, class T> SuffixTree<CharT,T>::SuffixTree():root_(0),leaves_(),nLeaves_(0),strings_(),latestInternalNode_(0),currentPos_(0),phase_(0),extension_(0) {root_ = allocNode();root_->parent = root_; // for convenienceroot_->link = root_;root_->len = 0;root_->str = 0;root_->startAt = 0;currentPos_ = root_; }template <class CharT, class T> SuffixTree<CharT,T>::SuffixTree(basic_string<CharT> const& str):root_(0),leaves_(),nLeaves_(0),strings_(),latestInternalNode_(0),currentPos_(0),phase_(0),extension_(0) {root_ = allocNode();root_->parent = root_; // for convenienceroot_->link = root_;root_->len = 0;root_->str = 0;root_->startAt = 0;currentPos_ = root_;this->addString(str); }template <class CharT, class T> void SuffixTree<CharT,T>::addString(CharT const* str, int len) {strings_.push_back(basic_string<CharT>(str, str+len));int whichString = strings_.size()-1;CharT const* cstr = strings_.back().c_str();dbg("adding string \"%s\" to suffix tree ... \n", cstr);root_->len = 0;root_->depth = 0;leaves_ = vector<NodeT*>();nLeaves_=0;currentPos_=root_;for(phase_=0; phase_<=len; ++phase_) { // there's a '\0' behindbool undone = true;latestInternalNode_ = 0;for(int j=nLeaves_; j <= phase_ && undone; ++j) {extension_ = j;undone = extend( cstr+extension_, phase_-extension_ );}}putEnd(); }template <class CharT, class T> typename SuffixTree<CharT,T>::NodeT* SuffixTree<CharT,T>:: jumpDown(CharT const* str, int len, NodeT* node, bool fromFront) {dbg("# jumping down path len=%d str=\"", len);dbg_puts(str,len);dbg("\" from node %08x ... \n", node);int i=0;if(fromFront) {if (node!=root_ && str[0]!=node->str[0]) {dbg("!!! cannot find such path !!!\n");throw(std::runtime_error("path not found"));}i = node->len != -1?node->len:phase_ - node->depth;}for(; i<len; ) {NodeIterator itr =std::find_if( node->branches.begin(),node->branches.end(),typename NodeT::MatchFirst(str[i]) );if(itr == node->branches.end()) {dbg("!!! cannot find such path !!!\n");throw(std::runtime_error("path not found"));} else {dbg("> going into node %08x which begins with \'%c\', len=%d,"" depth=%d\n",*itr, (*itr)->str[0], (*itr)->len, (*itr)->depth );node = *itr;if( node->len != -1 ) // internal nodei += node->len;else // leafi += phase_ - node->depth;}}if( i != len ) { // implicit nodeif(node->len != -1)node->startAt = node->len - (i-len);elsenode->startAt = phase_ - node->depth - (i-len);}dbg("< done.\n");return node; }template <class CharT, class T> typename SuffixTree<CharT,T>::NodeT* SuffixTree<CharT,T>::splitEdge(NodeT* node, CharT const* leafLabel /*len==-1*/) {/*** \* # <-- startAt* \* node** => => => => => => =>** \* \* newInternal* / \* / newLeaf (return value)* node**/dbg("X spliting edge %08x \n", node);NodeT* newLeaf = allocNode();NodeT* newInternal = allocNode();// newInternal->belongTo = node->belongTo;newInternal->startAt = 0;newInternal->len = node->startAt;newInternal->str = node->str;newInternal->parent = node->parent;newInternal->link = root_;newInternal->depth = node->depth;newInternal->branches.push_back(node);newInternal->branches.push_back(newLeaf);*(std::find(newInternal->parent->branches.begin(),newInternal->parent->branches.end(),node)) = newInternal;node->depth += node->startAt;node->str += node->startAt;node->parent = newInternal;if(node->len != -1) {node->len -= node->startAt;}node->startAt = 0;newLeaf->str = leafLabel;newLeaf->depth = node->depth;newLeaf->link = root_;newLeaf->len = -1;newLeaf->parent = newInternal;dbg(" new internal node = %08x ; \t new leaf = %08x\n",newInternal, newLeaf);return newLeaf; }template <class CharT, class T> bool SuffixTree<CharT,T>::extend(CharT const* str, int pos) {dbg("{{{ in phase %d, extension %d;\n", phase_, extension_);dbg(" adding \'%c\' to suffix tree ... \n", str[pos]);int const whichString = strings_.size()-1;int skiped = currentPos_->depth;NodeT *node = jumpDown(str+skiped, pos-skiped, currentPos_, true);if (node->startAt != 0) { // implicit nodedbg(" got implicit node %08x starts at %d\n", node, node->startAt);if(node->str[node->startAt] != str[pos]) {NodeT *newLeaf = splitEdge( node, str+pos );newLeaf->leafNum = extension_;newLeaf->belongTo=whichString;node = newLeaf->parent;if(latestInternalNode_) {latestInternalNode_->link = node;dbg("-> creating suffix link from %08x to %08x\n",latestInternalNode_,node);}latestInternalNode_ = node;currentPos_ = node->parent;leaves_.push_back(newLeaf);++nLeaves_;} else {dbg(" suffix already exists.\n}}} done.\n");node->startAt = 0;return false; // suffix already exists, all done.}} else { // explicit nodedbg(" got explicit node\n");if(latestInternalNode_) {latestInternalNode_->link = node;dbg("-> creating suffix link from %08x to %08x\n",latestInternalNode_,node);}latestInternalNode_ = node;if( std::find_if( node->branches.begin(),node->branches.end(),typename NodeT::MatchFirst(str[pos]) )!= node->branches.end()) {dbg(" suffix already exists.\n}}} done.\n");currentPos_ = node;return false; // all done.} else if(node->branches.empty() && node!=root_) {// a leaf created in the past, now it should be exdended.node->belongTo=whichString;node->str = str+pos-node->len;node->len = -1;currentPos_ = node->parent;node->leafNum = extension_;leaves_.push_back(node);++nLeaves_;} else {// add a leaf to existing explicit node.NodeT *newLeaf = allocNode();dbg(" adding new leaf %08x to node %08x.\n", newLeaf, node);newLeaf->parent = node;newLeaf->link = root_;newLeaf->str = str+pos;newLeaf->len = -1; // leafnewLeaf->belongTo=whichString;newLeaf->depth = node->depth + node->len;node->branches.push_back(newLeaf);currentPos_ = node;newLeaf->leafNum = extension_;leaves_.push_back(newLeaf);++nLeaves_;}}dbg("-> following suffix link from %08x to %08x ...\n",currentPos_,currentPos_->link );currentPos_ = currentPos_->link;dbg("}}} done.\n");return true; }static inline char* x08 (char* buf, void const * ptr) {sprintf(buf, "%08x", ptr);return buf; }template <class CharT, class T> static void __printSuffixTreeNode(Node<CharT,T>* node,int margin_left,int padding_left,std::basic_ostream<CharT> * stream) {int i,I;char addr[16];if( node->branches.empty()) {for(i=0; i<margin_left; ++i)*stream<<' ';*stream << '\"';for(i=0; i<node->len; ++i)*stream<< node->str[i];*stream << "\" " << x08(addr, node)<< "[" << addr << "] ";*stream << "\n";} else {for(i=0; i<margin_left; ++i)*stream<<' ';*stream << '\"';for(i=0; i<node->len; ++i)*stream<< node->str[i];*stream << "\" " << x08(addr, node)<< "[" << addr << "] ";*stream << ": {\n";for(i=0, I=node->branches.size(); i<I; ++i) {__printSuffixTreeNode(node->branches[i],margin_left+padding_left,padding_left, stream);}for(i=0; i<margin_left; ++i)*stream<<' ';*stream << "} \n";} }template <class CharT, class T> void SuffixTree<CharT,T>::print(std::basic_ostream<CharT> * stream) {assert(!"function not supported"); }template <> void SuffixTree<char,void*>::print(std::ostream * stream) // || // std::basic_ostream<char> {*stream<<"Root ";__printSuffixTreeNode<char,void*>(root_, 0,2, stream); }template <class T> static void __dotPrintNode(Node<char,T>const*node,std::ostream*stream) {char buf[16];int i,I,j;for(i=0,I=node->branches.size(); i<I; ++i) {Node<char,T> const* p = node->branches[i];*stream<<"node"<<x08(buf,p)<<" [label=\"\"";if(p->branches.empty())*stream<<" peripheries=2 width=0.1 height=0.1";*stream<<"];\nnode"<<x08(buf,node);*stream<<" -> node"<<x08(buf,p)<<" [label=\"";for(j=0;j<p->len;++j)if(p->str[j])*stream<<p->str[j];else*stream<<"$";*stream<<"\"];\n";__dotPrintNode(p,stream);}if(!node->branches.empty()) {*stream<<"node"<<x08(buf,node);*stream<<" -> node"<<x08(buf, (node->link))<<" "<<"[color=red];\n";} }template <class CharT, class T> void SuffixTree<CharT,T>::dot(std::basic_ostream<CharT> *) {assert(!"function not supported"); }template <> void SuffixTree<char,void*>::dot(std::ostream * stream) {char buf[16];*stream<<"digraph stree {\n";*stream<<"node"<<x08(buf, root_)<<" [label=\"root\"];\n";*stream<<"node"<<buf<<" -> node"<<buf<<";\n";*stream<<"node [width=0.2 height=0.2] \n";__dotPrintNode<void*>(root_, stream);*stream<<"}\n"; }#endif /* SUFFIXTREE_H */// vi:ts=4:foldmethod=marker

    7???References

    [1]: Esko Ukkonen, Suffix tree and suffix array techniques for pattern analysis in strings, Erice School 30 Oct 2005
    [2](1,?2,?3,?4)?: Gusfield, Dan (1999) [1997]. Algorithms on Strings, Trees and Sequences: Computer Science and Computational Biology. USA: Cambridge University Press. ISBN 0-521-58519-8
    [3](1,?2)?: E. Ukkonen, On-Line Construction of Suffix Trees, Algorithmica, 14 (1995), 249-260

    轉載于:https://www.cnblogs.com/sandy2013/p/3260009.html

    總結

    以上是生活随笔為你收集整理的suffix tree的全部內容,希望文章能夠幫你解決所遇到的問題。

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

    日韩精品一区二区三区水蜜桃 | 国产视频 亚洲精品 | 久草av在线播放 | 综合久久久久久久 | 黄色av一区二区 | 婷婷六月天天 | 亚洲精品中文字幕视频 | 久久在草 | 久久99这里只有精品 | 欧美va天堂在线电影 | 国产一区在线精品 | 91看毛片 | 午夜黄色影院 | 久久激情日本aⅴ | 女人18毛片a级毛片一区二区 | 亚洲爽爽网 | 日韩簧片在线观看 | 成年人黄色免费网站 | 午夜视频在线观看一区 | 日韩免费观看一区二区三区 | 久久精品波多野结衣 | 最近中文字幕免费视频 | 国内亚洲精品 | 国产一区二区视频在线播放 | 国产一级一片免费播放放a 一区二区三区国产欧美 | 奇米网在线观看 | 国产网红在线观看 | 国产亚洲片 | 在线观看岛国片 | 中文字幕资源在线 | 97国产电影 | 日韩av免费观看网站 | 久久伦理 | 人人干天天射 | 久久久www免费电影网 | 五月婷av | 亚洲精品影视在线观看 | 亚洲最大免费成人网 | 亚洲视频在线免费看 | 麻豆国产精品一区二区三区 | 欧美成人中文字幕 | 久久不卡av| 国产一级精品在线观看 | 国产区高清在线 | 国产69熟| 99精品国产在热久久下载 | 特黄一级毛片 | 亚洲 欧美 精品 | 91看片在线播放 | 国产福利91精品一区 | 97超碰成人在线 | 伊人午夜 | 97视频亚洲 | 97国产精品久久 | 国产精品99久久久久久大便 | 久久久99精品免费观看乱色 | 97在线看 | 中文字幕国内精品 | 夜色在线资源 | 国产黄在线看 | 国产精品三级视频 | 91视频久久 | 天天综合天天做天天综合 | 97人人澡人人爽人人模亚洲 | 男女啪啪网站 | 欧美日bb| 欧美资源 | 国产拍揄自揄精品视频麻豆 | 日本黄色a级大片 | 成人久久久精品国产乱码一区二区 | 日韩av二区 | 岛国大片免费视频 | 日日夜夜天天人人 | 精品99在线视频 | 国产一级片在线播放 | 亚洲国产精品推荐 | 手机版av在线 | 久久五月婷婷丁香 | 三级大片网站 | 国产精品99久久久久久宅男 | 丁香六月激情婷婷 | 成人毛片在线观看视频 | 色偷偷av男人天堂 | 操老逼免费视频 | 久久久综合九色合综国产精品 | 欧美午夜激情网 | 国产亚洲精品中文字幕 | 超碰在线cao | 亚洲成人xxx | 国产亚洲精品av | 久久电影网站中文字幕 | 午夜视频一区二区 | 99热这里只有精品久久 | 超碰97免费在线 | www.久久成人 | 色综合五月天 | 中文字幕一区二区在线播放 | av+在线播放在线播放 | 日韩免费中文字幕 | 国产一级精品在线观看 | av大全在线免费观看 | 天天干天天操天天入 | 欧美日韩性 | 久久夜色精品国产欧美一区麻豆 | 成人免费观看视频网站 | 久久视频在线观看中文字幕 | 91成熟丰满女人少妇 | 精品嫩模福利一区二区蜜臀 | 精品在线观看一区二区 | 国产精品久久久久aaaa | 天天艹 | 超碰个人在线 | av免费看在线 | 国产在线视频一区二区三区 | 久99久在线视频 | 中文久草 | 国产视频午夜 | 精品视频久久久久久 | 欧美日韩精品免费观看 | 天天干夜夜爱 | 亚洲自拍自偷 | 91在线最新 | 国产不卡片 | 久久全国免费视频 | 日韩视频在线不卡 | 久久免费在线观看视频 | 香蕉视频久久久 | 国色天香第二季 | 午夜精品一二三区 | 国产视频91在线 | 丁香婷婷色 | 国产麻豆精品一区二区 | 91av视频观看 | 日日碰狠狠添天天爽超碰97久久 | 国产精品门事件 | 国产精品涩涩屋www在线观看 | 亚洲 在线 | 日韩av黄 | 日韩免费福利 | 国产日韩精品一区二区三区在线 | 高清av在线| 五月天激情综合 | av在线免费网 | 人人超在线公开视频 | 久久激五月天综合精品 | 91福利在线观看 | 在线观看视频国产一区 | 丁香国产视频 | 亚洲黄色片在线 | av中文字幕在线免费观看 | 免费精品视频 | 色香天天| 激情欧美一区二区三区 | 久草精品视频在线观看 | 福利av影院 | 天天综合网 天天综合色 | 国产精品自拍在线 | 五月婷婷天堂 | 天天射天天干天天 | 九九热久久久 | 久久免费的精品国产v∧ | 免费在线h | 日日夜夜网站 | 亚洲高清免费在线 | 久久综合久久综合这里只有精品 | 久久一区二区三区日韩 | 久久在线观看视频 | 91精品视频导航 | bbb搡bbb爽爽爽 | 久久精品79国产精品 | avav片| av不卡网站 | 黄色的视频 | av大全在线播放 | 日韩欧美69 | 免费观看一级一片 | 成人av资源网 | 国产专区在线视频 | 日韩精品欧美专区 | 成人国产精品一区 | 欧美激情综合五月色丁香 | 亚洲一区精品二人人爽久久 | 久久亚洲综合国产精品99麻豆的功能介绍 | 欧美成人在线免费 | 亚洲精品在线免费观看视频 | 丁香六月婷 | 麻豆视频免费播放 | 欧美一区二区免费在线观看 | 国产精品一区二区久久精品爱微奶 | 亚洲一区免费在线 | 精品a级片 | 中文字幕在线高清 | 国产91精品在线播放 | 国产黄色片久久久 | 婷婷国产在线观看 | 人人爽人人乐 | 久久99亚洲精品 | 中文字幕日本电影 | 免费在线观看亚洲视频 | av福利在线播放 | 久久精品电影院 | 免费成人在线网站 | 91在线视频观看免费 | 婷婷伊人综合亚洲综合网 | 在线日本看片免费人成视久网 | 五月开心婷婷网 | 永久精品视频 | 欧美亚洲久久 | 久久久久北条麻妃免费看 | 日韩电影一区二区三区 | 欧美日韩国产一区二区三区在线观看 | 欧美成人在线免费观看 | 亚洲人片在线观看 | 色婷婷综合在线 | 国产丝袜制服在线 | 天天狠狠干 | 在线播放日韩 | 免费在线激情电影 | 免费美女久久99 | 天天干天天干天天操 | 久久伊人八月婷婷综合激情 | 97视频在线免费观看 | 美女视频黄是免费的 | 香蕉网站在线观看 | 日韩在线第一区 | av成人免费 | 日韩特级黄色片 | 午夜精品99久久免费 | 激情动态| 超碰在线94 | 中文字幕一区在线观看视频 | 亚洲免费av一区二区 | 午夜私人影院久久久久 | 免费成人av网站 | 成人av高清 | 久久免费的精品国产v∧ | 狠狠狠色丁香婷婷综合久久五月 | 免费久久网 | 成人在线免费看视频 | 人人澡人人爱 | 在线影院 国内精品 | 久艹在线观看视频 | 天天操天天玩 | 91传媒在线看 | av片一区二区| av在线免费观看网站 | 国产精品一区二区三区在线看 | 99精彩视频 | 久草在线视频免费资源观看 | 欧美日韩精品影院 | 日韩欧美一区二区三区视频 | 亚洲精品一区二区三区高潮 | 97精产国品一二三产区在线 | 日韩在线网址 | 在线观看mv的中文字幕网站 | 国产一区在线视频观看 | 国产美女精品久久久 | 日韩在线观看视频一区二区三区 | av成人亚洲 | 在线视频你懂得 | 激情视频一区二区三区 | 九九热在线播放 | 亚洲91精品在线观看 | 亚洲精品国偷拍自产在线观看蜜桃 | 国产999精品久久久久久绿帽 | 在线免费性生活片 | 欧美精品999 | 91av九色 | 久久精品91视频 | 久久久免费精品 | 国产精品成人一区二区三区吃奶 | 久久免费精品一区二区三区 | 香蕉免费在线 | 国产精品私人影院 | 精品96久久久久久中文字幕无 | 午夜av影院 | 毛片在线播放网址 | av电影免费在线看 | 99九九热只有国产精品 | 国产精品福利小视频 | 9999国产精品 | 精品综合久久久 | 视频一区在线免费观看 | 91人人干| 女女av在线 | 亚洲欧美日韩在线看 | 日本中文一级片 | 久久 一区 | 99热精品在线 | 国产精品久久久久久久久久久久午夜 | 国产精品久久久久久久久久了 | 狠狠狠色丁香综合久久天下网 | 国产黄色片久久久 | 成年美女黄网站色大片免费看 | 日韩理论在线视频 | 国产盗摄精品一区二区 | www激情网| www四虎影院 | 国产精品一区二区三区99 | 人人超碰97| 日韩一级成人av | 国产无区一区二区三麻豆 | 三级av中文字幕 | 丁香 久久 综合 | 91av电影在线观看 | 亚洲一区 影院 | 久草在线高清 | 在线一级片 | 成人动漫一区二区三区 | 深夜福利视频在线观看 | 久久久久久久免费看 | 国产99在线免费 | av看片在线观看 | 日本精品视频一区二区 | 国产在线精品一区二区 | 国产成人一区二区三区在线观看 | 国产99在线免费 | 91激情视频在线观看 | 97免费中文视频在线观看 | 99精品久久精品一区二区 | 五月激情久久久 | 成人精品视频 | 天天操狠狠操夜夜操 | 美女免费网站 | 91九色视频在线 | 天天操天天爽天天干 | 91成人破解版 | 亚洲另类视频在线观看 | 中文一区在线观看 | 99c视频高清免费观看 | 午夜视频在线观看一区 | 久久视频在线看 | 国产精品久久久99 | 天天天干天天天操 | 国产资源免费在线观看 | 成人免费视频网站在线观看 | 久久综合九色综合欧美就去吻 | 久久国产精品久久精品国产演员表 | 国产小视频在线观看 | 亚洲精品456在线播放 | 国产亚洲精品久久久久久 | 国产欧美高清 | 午夜精品久久久久久久久久久久 | 一区二区精品在线 | 国产欧美中文字幕 | 中文字幕你懂的 | 亚洲欧美国产精品va在线观看 | 日韩色视频在线观看 | 狠狠做深爱婷婷综合一区 | 色狠狠干 | 97国产超碰在线 | 亚洲一区视频在线播放 | 久久av免费 | 黄色动态图xx | 国产a级片免费观看 | 97超碰人人澡人人爱学生 | 97成人在线免费视频 | 国产成人一区二区三区在线观看 | 国产美女精品久久久 | 色亚洲激情 | 蜜臀av性久久久久蜜臀aⅴ四虎 | 日韩欧美亚州 | 日韩高清一区在线 | 一级a性色生活片久久毛片波多野 | a级国产乱理伦片在线观看 亚洲3级 | 国产伦理剧 | 久久国产精品网站 | 综合色站| 国产一级免费片 | 久久不卡视频 | 91亚洲国产成人久久精品网站 | 久久丁香网 | 97网在线观看 | 国产专区精品 | 色婷婷亚洲 | 成人国产在线 | 一区二区三区四区不卡 | 国产做a爱一级久久 | 国产午夜一级毛片 | 香蕉久草 | 色综合色综合久久综合频道88 | 91精品免费视频 | 韩日av在线 | 久久综合免费视频影院 | 伊人狠狠色丁香婷婷综合 | 国产做a爱一级久久 | 国产在线一区二区 | 波多野结衣在线观看一区二区三区 | 91av视频在线观看免费 | 国产三级视频在线 | 美女久久久久久久久久 | 日本精品久久久久 | 久久久综合香蕉尹人综合网 | 欧美性生活大片 | 首页国产精品 | 九九在线视频 | 日韩综合一区二区三区 | 夜色成人网 | 日本三级国产 | 欧美在线18 | 免费成人av | 精品一二区 | 一级精品视频在线观看宜春院 | 亚洲狠狠婷婷综合久久久 | 欧女人精69xxxxxx | 国产免费xvideos视频入口 | av资源免费在线观看 | 国产 一区二区三区 在线 | 日韩美精品视频 | 美女久久久久久久久久 | 麻花豆传媒mv在线观看网站 | 久久免费精品一区二区三区 | 麻豆视频在线免费看 | 国产在线播放不卡 | 国产精品va在线 | 国产免费一区二区三区最新 | 中文字幕精品一区二区三区电影 | 九九热精品视频在线观看 | 久爱精品在线 | 成人久久18免费网站图片 | 日韩视频专区 | 国产探花| 久久久久久久久久网站 | 国产电影黄色av | 欧美日韩视频在线观看免费 | 一性一交视频 | 国产精品18久久久久久久网站 | 91chinese在线 | 又黄又刺激的视频 | 日日干夜夜操视频 | 黄色免费网站下载 | 日韩久久精品一区二区三区 | 日韩国产在线观看 | 亚洲精品av在线 | 亚洲精品在线看 | 中文字幕制服丝袜av久久 | 色婷婷影视 | 视频一区在线播放 | 欧美日韩在线视频一区 | 噜噜色官网 | 日韩高清黄色 | 综合色站 | 日韩av影片在线观看 | 最近日本中文字幕a | 国产精品国产亚洲精品看不卡15 | 久久久久久久久电影 | 日本丰满少妇免费一区 | 97成人精品区在线播放 | 欧美男同网站 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 国产午夜麻豆影院在线观看 | 亚洲日韩中文字幕 | 99久久精品日本一区二区免费 | 精品国产资源 | 国产成人精品a | 国产黄色精品在线观看 | 日韩av不卡在线 | 欧美最爽乱淫视频播放 | 成人资源在线观看 | 中文字幕在线一二 | 亚洲免费在线观看视频 | 丁香花中文字幕 | 国产高清在线精品 | 中文字幕资源网 国产 | 日韩欧美在线视频一区二区三区 | 黄色成人在线观看 | 久久久99精品免费观看app | 国产精品原创 | 综合激情婷婷 | 国产一区二区在线看 | 91视频久久 | 国内精品久久久久国产 | 久久久片| 91精品啪在线观看国产81旧版 | 久久亚洲私人国产精品 | 国产综合精品一区二区三区 | 午夜色场 | 久久免费影院 | 日本黄色免费看 | 日日摸日日添日日躁av | 在线看国产精品 | 97超级碰| 夜添久久精品亚洲国产精品 | 日韩精品在线看 | 97在线看| 日韩毛片一区 | 欧美综合在线视频 | 九色porny真实丨国产18 | 97在线看 | 中文乱幕日产无线码1区 | 香蕉视频最新网址 | 国产精品夜夜夜一区二区三区尤 | 亚洲一级电影 | 97视频在线免费 | 国产精品99久久久久 | 97人人模人人爽人人喊中文字 | 国产美女精品久久久 | 天天干.com| 香蕉影视在线观看 | 伊人天堂久久 | 最新精品视频在线 | 成人黄色免费在线观看 | 久久婷综合 | 91精品久久香蕉国产线看观看 | 久久久久网站 | 国产高清 不卡 | 亚洲精品网站在线 | 在线免费视 | 中文字幕久久久精品 | 日韩理论视频 | 欧美性大战 | 九九在线播放 | 又长又大又黑又粗欧美 | 人人射人人 | 中文在线亚洲 | 欧美成人xxxxx| www.啪啪.com| 亚洲 欧美变态 另类 综合 | 精品在线观看一区二区 | 手机在线看永久av片免费 | 激情丁香婷婷 | 99在线观看视频网站 | 二区视频在线观看 | 永久免费毛片 | 麻豆成人小视频 | 天天操天天操天天操天天操天天操天天操 | 久草精品网 | 国产一区二区播放 | 亚洲一区欧美激情 | 激情亚洲综合在线 | 国产午夜精品一区二区三区在线观看 | 亚洲精品免费在线观看视频 | 久艹在线免费观看 | 日本成址在线观看 | 久久99婷婷 | 首页国产精品 | 91网免费看 | 精品一区二区在线免费观看 | 黄污视频网站大全 | 精品一区二区三区电影 | 亚洲精品国产品国语在线 | 欧美伦理电影一区二区 | 日韩高清一区 | 免费福利在线视频 | 久久久视屏 | 久久成人国产精品免费软件 | 久久精品高清 | 国产原创中文在线 | 黄色一级动作片 | 日韩视频一区二区 | 美女视频黄网站 | 国产香蕉97碰碰碰视频在线观看 | 久久久资源 | 国产资源网 | 一区二区三区四区不卡 | 国产成人综合在线观看 | 日韩乱色精品一区二区 | 二区三区在线观看 | 国产精品永久在线 | 97视频中文字幕 | 久久国产影院 | 最新成人在线 | 久久精品99国产国产精 | 在线小视频国产 | 国产精品成人免费一区久久羞羞 | 国产色女 | 欧美日韩国产伦理 | 一区二区不卡高清 | 久久在线一区 | 日日夜精品 | 国产91九色视频 | 丁香六月久久综合狠狠色 | 国产精品 999 | 久久久久伊人 | 亚洲 中文字幕av | 国产成年免费视频 | 亚洲精品videossex少妇 | 国产精品9区| 中文字幕亚洲在线观看 | 日韩欧美在线视频一区二区 | 国产美女在线精品免费观看 | 日韩黄色中文字幕 | 中国一 片免费观看 | 免费看黄色大全 | 99久久久久久国产精品 | 一级黄色免费 | 久久久黄视频 | 亚洲一区日韩 | 欧美另类一二三四区 | 麻豆国产精品va在线观看不卡 | 天天舔天天射天天操 | 国产麻豆精品一区 | 国产精品va视频 | 91久草视频 | 九九热精品视频在线观看 | 日韩成人高清在线 | 成人综合免费 | 国产精品女人久久久 | 中文字幕日本电影 | 欧美性极品xxxx娇小 | 美女久久久久久 | 国产精品扒开做爽爽的视频 | 97超碰在线久草超碰在线观看 | 天堂av中文字幕 | 久久不见久久见免费影院 | 久久99久久99久久 | 日韩二区精品 | 中文在线a√在线 | 久久av高清 | 日韩av电影网站在线观看 | 国产精品视频线看 | 国产一区二区三区免费视频 | 日韩免费三区 | 久久深夜福利免费观看 | 国产精品系列在线观看 | 久操中文字幕在线观看 | 国产成人精品一区二区 | 在线之家免费在线观看电影 | 日韩在线视频看看 | 丁五月婷婷| www.97色.com| 在线视频精品 | 五月天com | 日韩一区二区三区在线观看 | 久久综合五月天 | 欧美一级片在线 | 国产中文字幕在线播放 | 欧美另类人妖 | 免费观看一区二区三区视频 | 国产一区二区视频在线播放 | 九草视频在线观看 | 操处女逼 | 91亚洲在线观看 | 五月婷久| 亚洲综合五月 | 91精品国产网站 | 国产黑丝一区二区三区 | 91视频91蝌蚪| 亚洲最新在线 | www久久久久 | 久久久不卡影院 | 中文字幕在线影院 | 亚洲日本精品视频 | 欧美精品久久久久久久 | 免费网站在线观看成人 | a级国产乱理论片在线观看 特级毛片在线观看 | 午夜丰满寂寞少妇精品 | 精品久久久久久久久久久院品网 | 欧美日韩一区二区在线 | 国产亚洲视频在线免费观看 | 亚洲精品乱码久久久久久写真 | 免费裸体视频网 | 日韩天天综合 | 九色在线视频 | 日韩欧美有码在线 | 韩日成人av | 91人人插| 国产精品亚洲人在线观看 | 五月婷婷六月丁香激情 | 欧美一区二区精美视频 | 久久久久99999| 500部大龄熟乱视频使用方法 | 色资源二区在线视频 | 二区三区av | 欧美日韩免费一区二区 | 九七人人干 | 日韩精品免费在线播放 | 国产无遮挡又黄又爽在线观看 | 视频成人 | 成人a级免费视频 | 九色精品在线 | 国产精品一区二区av麻豆 | 91免费版在线 | 精品国产一区二区久久 | 91 中文字幕 | 色婷婷九月 | 综合激情网 | 久久精品8 | 亚洲精品www久久久久久 | 中文字幕日本电影 | 91超在线| 日韩高清国产精品 | 亚欧日韩av | 国产粉嫩在线观看 | 亚洲精品一区二区18漫画 | 99人成在线观看视频 | 久久婷婷国产 | 久色免费视频 | 成人在线免费视频 | 在线视频日韩欧美 | 91香蕉视频黄 | 国产精品国产自产拍高清av | 久久综合免费 | 婷婷综合亚洲 | 麻豆激情电影 | 国产精品一区二区免费在线观看 | 在线中文字幕观看 | 国产一级a毛片视频爆浆 | 亚洲黄色免费在线看 | 久久在线免费视频 | 在线播放 日韩专区 | 国内精品视频在线 | 色网站视频 | 黄色日本免费 | 国产高清av免费在线观看 | 三级a视频| 日韩字幕在线观看 | 日韩成人精品 | 国产成人一区二区啪在线观看 | 欧美日韩18| 美女黄频在线观看 | 深爱激情五月综合 | 天天操天天操天天操 | 国产一区二区视频在线播放 | 深爱激情综合 | www婷婷| 日韩超碰在线 | 久久综合亚洲鲁鲁五月久久 | 天天插综合 | 91av中文字幕 | 欧美一级片在线观看视频 | 九精品 | 国产高清av | 黄色片视频在线观看 | 美女黄色网在线播放 | 成人午夜电影久久影院 | 超碰九九| 波多野结衣在线观看一区二区三区 | 天天av天天| 国产精品99久久久久久宅男 | 9在线观看免费高清完整版在线观看明 | 91麻豆精品国产91久久久使用方法 | 9热精品| 国产一区不卡在线 | 亚洲高清国产视频 | 国产在线不卡一区 | 天天草天天干 | 中文字幕中文中文字幕 | 91精品网站在线观看 | 精品国产理论片 | 久久精品牌麻豆国产大山 | 亚洲第一区在线观看 | 色婷婷国产 | 久久视频这里有久久精品视频11 | 欧美黑人xxxx猛性大交 | 日韩av黄| 国产精品国产三级国产 | 蜜桃麻豆www久久囤产精品 | 在线成人性视频 | 国产精品久久久久久久7电影 | 亚洲精品视频观看 | 日韩欧美视频在线观看免费 | 人人爽人人爽人人爽人人爽 | 麻豆视频免费在线 | 国产黄色一级大片 | 亚洲高清色综合 | 日本免费一二三区 | 国产粉嫩在线 | 亚洲成人网av | 女人18片毛片90分钟 | 国产高清av免费在线观看 | 国产精品一区二区精品视频免费看 | 国产黄色片久久 | 中文字幕精品久久 | 九九国产视频 | 亚洲国产三级在线 | 2021国产精品| 久久艹国产视频 | 日韩v在线91成人自拍 | 99热官网| 午夜婷婷在线播放 | 欧美日韩另类在线观看 | 国产午夜精品福利视频 | 高清不卡一区二区在线 | 色小说av| 国产精品高潮呻吟久久av无 | 欧美精品一区二区三区四区在线 | 久久久精品电影 | 人人舔人人射 | 国产精品福利在线观看 | 日韩小视频网站 | 91超碰在线播放 | 久久久96 | 亚洲 中文 在线 精品 | 九九免费在线观看 | 久久av影视 | 亚洲最大成人网4388xx | 国产在线国偷精品产拍 | 欧美一级电影免费观看 | 草久在线 | 综合色中文 | 国产99久久久欧美黑人 | 午夜精品999 | 欧美在线一 | 日韩城人在线 | 色www永久免费 | 国产成人在线综合 | 国产精品久久久久久婷婷天堂 | 亚洲电影免费 | 国产最新在线观看 | 亚洲精品在线观看免费 | 免费观看特级毛片 | 午夜私人影院久久久久 | 日韩一区二区三区高清免费看看 | 制服丝袜在线 | 免费成人在线网站 | 亚洲欧美成人 | 久久五月天色综合 | 亚洲无吗视频在线 | 国产精品免费视频久久久 | 欧美激情精品一区 | 天天操操操操操操 | www.国产精品 | 久久久国产日韩 | 日韩成人免费观看 | 久久免费精品 | 精品毛片久久久久久 | 亚洲视频 视频在线 | 国产中文字幕一区 | 亚洲精品视频网站在线观看 | 精品成人网 | 性色av免费在线观看 | 免费国产一区二区 | 久久国产二区 | 国产一区二区三区免费视频 | 91精品网站在线观看 | 日韩免费久久 | 久久人人看 | 天天干天天拍天天操天天拍 | 久久综合婷婷综合 | www.com.日本一级 | 九九在线免费视频 | 国产精品永久免费在线 | 丁香5月婷婷 | 中文字幕色综合网 | 热久久这里只有精品 | 国产精品免费久久 | 国产黄免费在线观看 | 色视频在线 | 亚洲精品videossex少妇 | 456成人精品影院 | 国产黄色精品 | 成人精品久久 | 欧美日韩中文字幕在线视频 | 精品福利视频在线观看 | 亚洲精品美女在线观看 | 黄色精品视频 | 国产色视频网站 | 亚洲国产精品女人久久久 | 色婷婷久久久综合中文字幕 | 中文字幕丝袜美腿 | 午夜精品久久久久久久99热影院 | 在线激情影院一区 | 三级在线视频播放 | 久久天堂亚洲 | 亚洲综合婷婷 | 少妇bbw撒尿 | 免费福利在线 | 久操97 | 97超碰在线免费 | 97超碰人人澡人人爱学生 | av电影一区二区三区 | 在线观看av中文字幕 | 不卡精品 | 亚洲精区二区三区四区麻豆 | 精品久久五月天 | a'aaa级片在线观看 | 精品少妇一区二区三区在线 | 在线观看视频亚洲 | 欧美做受高潮 | 中文字幕在线观看一区二区三区 | 婷婷激情综合五月天 | 久久久三级视频 | 人人干天天干 | 国产va饥渴难耐女保洁员在线观看 | 欧美日韩国产免费视频 | 99久久这里有精品 | 久艹在线免费观看 | 五月婷网站 | 精品产品国产在线不卡 | 久草在线 | 日本三级在线观看中文字 | 国产精品一区免费观看 | 欧美综合久久久 | 日本中文字幕观看 | 亚洲精品免费在线视频 | 亚洲欧美国产精品va在线观看 | 2021国产精品视频 | 亚洲电影毛片 | 91精彩视频在线观看 | 午夜在线观看一区 | 在线三级av | 国产精品一区免费在线观看 | 91成年人网站 | 久久a国产 | 国产精品永久在线观看 | 午夜黄色一级片 | 欧美最猛性xxxx | 亚洲天堂自拍视频 | 欧美日韩电影在线播放 | 午夜精品久久久久 | 在线看不卡av | 一区二区三区高清在线 | 国产精品黄色 | 精品亚洲一区二区 | 国产精品色婷婷视频 | 国产精久久 | 99久久久国产精品美女 | 久久免费看a级毛毛片 | 国产精品18久久久久久久网站 | 亚洲三级在线 | 国产亚洲欧美在线视频 | 国产一级特黄毛片在线毛片 | 五月婷婷另类国产 | 91亚洲激情 | 中文字幕 国产专区 | 亚洲精品国产成人av在线 | 激情综合色图 | 一本色道久久精品 | 99久久精品日本一区二区免费 | 成人小视频在线观看免费 | 伊人久久电影网 | 国产一区二区三区高清播放 | 91av网站在线观看 | 国产第一页在线观看 | 久久精品看 | a级片韩国| 精品二区久久 | 97在线看 | 国产精品久久久区三区天天噜 | 欧美a√大片 | 女人18毛片a级毛片一区二区 | 免费午夜视频在线观看 | 91资源在线视频 | av一级在线 | 亚洲欧美在线视频免费 | 国产高清在线 | 久久久久二区 | 在线成人观看 | 久久专区 | 国产999精品视频 | 欧美污污网站 | 色网站中文字幕 | 一区二区三区视频网站 | 91精品一区在线观看 | 五月综合激情婷婷 | 国产高清在线不卡 | 91看成人 | 国产精品v欧美精品v日韩 | 国产xx视频 | 右手影院亚洲欧美 | 中文国产字幕在线观看 | 麻豆国产露脸在线观看 | 日本精品一区二区在线观看 | 91视频免费看片 | 亚洲区二区 | 亚洲欧美激情精品一区二区 | 亚洲毛片久久 | 美女久久99 | 日本大尺码专区mv | 欧美日本不卡 | 四虎影视av| 日韩中文在线字幕 | 97精品国产97久久久久久久久久久久 | 精品国内自产拍在线观看视频 | 国产自偷自拍 | 开心激情综合网 | 91九色最新地址 | 久草新在线 | 黄色在线看网站 | 天天操网 | 婷婷av电影 | 亚洲欧美在线视频免费 | 毛片网站在线看 | 国产在线观看高清视频 | 99久久久久国产精品免费 | 欧美日韩国产欧美 | 玖玖视频网 | 亚洲涩涩网| 欧美另类亚洲 | 日韩三级视频在线观看 | av在线播放快速免费阴 | 在线免费中文字幕 | 日韩在线看片 | 亚洲天堂精品视频在线观看 | 最新日韩视频 | 色视频网站免费观看 | 黄色毛片视频免费观看中文 | 国产中文在线字幕 | 欧美日韩在线观看一区二区 | 99re视频在线观看 | 色国产视频 | 国产成人一区二区三区久久精品 | 久久9精品| 久久精品在线免费观看 | 91av视频播放 | 青青河边草免费观看完整版高清 | 久草视频在线资源 |