1. 程式人生 > >Palindromic Tree——迴文樹【處理一類迴文串問題的強力工具】

Palindromic Tree——迴文樹【處理一類迴文串問題的強力工具】

今天我們來學習一個神奇的資料結構:Palindromic Tree。中譯過來就是——迴文樹。

那麼這個迴文樹有何功能?

假設我們有一個串S,S下標從0開始,則迴文樹能做到如下幾點:

1.求串S字首0~i內本質不同迴文串的個數(兩個串長度不同或者長度相同且至少有一個字元不同便是本質不同)

2.求串S內每一個本質不同迴文串出現的次數

3.求串S內迴文串的個數(其實就是1和2結合起來)

4.求以下標i結尾的迴文串的個數

那麼我們該如何構造迴文樹?

首先我們定義一些變數。

1.len[i]表示編號為i的節點表示的迴文串的長度(一個節點表示一個迴文串)

2.next[i][c]表示編號為i的節點表示的迴文串在兩邊新增字元c以後變成的迴文串的編號(和字典樹類似)。

3.fail[i]表示節點i失配以後跳轉不等於自身的節點i表示的迴文串的最長字尾迴文串(和AC自動機類似)。

4.cnt[i]表示節點i表示的本質不同的串的個數(建樹時求出的不是完全的,最後count()函式跑一遍以後才是正確的)

5.num[i]表示以節點i表示的最長迴文串的最右端點為迴文串結尾的迴文串個數。

6.last指向新新增一個字母后所形成的最長迴文串表示的節點。

7.S[i]表示第i次新增的字元(一開始設S[0] = -1(可以是任意一個在串S中不會出現的字元))。

8.p表示新增的節點個數。

9.n表示新增的字元個數。

一開始迴文樹有兩個節點,0表示偶數長度串的根和1表示奇數長度串的根

,且len[0] = 0,len[1] = -1,last = 0,S[0] = -1,n = 0,p = 2(添加了節點0、1)。



假設現在我們有串S = abbaabba。

首先我們新增第一個字元'a',S[++ n] = 'a',然後判斷此時S[n - len[last] - 1]是否等於S[n],即上一個串-1的位置和新新增的位置是否相同,相同則說明構成迴文。否則,last = fail[last]。此時last = 0,我們發現S[1 - 0 - 1] != S[1],所以last = fail[last] = 1,然後我們發現S[1 - (-1) - 1] == S[1](即自己等於自己,所以我們讓len[1]等於-1可以讓這一步更加方便)。

令cur等於此時的last(即cur = last = 1),判斷此時next[cur]['a']是否已經有後繼,如果next[cur]['a']沒有後繼,我們就進行如下的步驟:新建節點(節點數p++,且之後p = 3),並讓now等於新節點的編號(now = 2),則len[now] = len[cur] + 2(每一個迴文串的長度總是在其最長子迴文串的基礎上在兩邊加上兩個相同的字元構成的,所以是+2,同時體現出我們讓len[1] = -1的優勢,一個字元自成一個奇迴文串時迴文串的長度為(-1) + 2 = 1)。然後我們讓fail[now] = next[get_fail ( fail[cur] )]['a'],即得到fail[now](此時為fail[2] = 0),其中的get_fail函式就是讓找到第一個使得S[n - len[last] - 1] == S[n]的last。然後next[cur]['a'] = now。

當上面步驟完成後我們讓last = next[cur][c](不管next[cur]['a']是否有後繼),然後cnt[last] ++。

此時迴文樹為下圖狀態:


現在我們新增第二個字元字元'b'到迴文樹中:

繼續新增第三個字元'b'到迴文樹中:


繼續新增第四個字元'a'到迴文樹中:


繼續新增第五個字元'a'到迴文樹中:


繼續新增第六個字元'b'到迴文樹中:



繼續新增第七個字元'b'到迴文樹中:


繼續新增第八個字元'a'到迴文樹中:


到此,串S已經完全插入到迴文樹中了,現在所有的資料如下:

然後我們將節點x在fail指標樹中將自己的cnt累加給父親,從葉子開始倒著加,最後就能得到串S中出現的每一個本質不同迴文串的個數。

構造迴文樹需要的空間複雜度為O(N*字符集大小),時間複雜度為O(N*log(字符集大小)),這個時間複雜度比較神奇。如果空間需求太大,可以改成鄰接表的形式儲存,不過相應的要犧牲一些時間。

總的來說,這是一個很好的演算法~

下面給上我的code:

const int MAXN = 100005 ;
const int N = 26 ;

struct Palindromic_Tree {
	int next[MAXN][N] ;//next指標,next指標和字典樹類似,指向的串為當前串兩端加上同一個字元構成
	int fail[MAXN] ;//fail指標,失配後跳轉到fail指標指向的節點
	int cnt[MAXN] ;
	int num[MAXN] ;
	int len[MAXN] ;//len[i]表示節點i表示的迴文串的長度
	int S[MAXN] ;//存放新增的字元
	int last ;//指向上一個字元所在的節點,方便下一次add
	int n ;//字元陣列指標
	int p ;//節點指標

	int newnode ( int l ) {//新建節點
		for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
		cnt[p] = 0 ;
		num[p] = 0 ;
		len[p] = l ;
		return p ++ ;
	}

	void init () {//初始化
		p = 0 ;
		newnode (  0 ) ;
		newnode ( -1 ) ;
		last = 0 ;
		n = 0 ;
		S[n] = -1 ;//開頭放一個字符集中沒有的字元,減少特判
		fail[0] = 1 ;
	}

	int get_fail ( int x ) {//和KMP一樣,失配後找一個儘量最長的
		while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
		return x ;
	}

	void add ( int c ) {
		c -= 'a' ;
		S[++ n] = c ;
		int cur = get_fail ( last ) ;//通過上一個迴文串找這個迴文串的匹配位置
		if ( !next[cur][c] ) {//如果這個迴文串沒有出現過,說明出現了一個新的本質不同的迴文串
			int now = newnode ( len[cur] + 2 ) ;//新建節點
			fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自動機一樣建立fail指標,以便失配後跳轉
			next[cur][c] = now ;
			num[now] = num[fail[now]] + 1 ;
		}
		last = next[cur][c] ;
		cnt[last] ++ ;
	}

	void count () {
		for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
		//父親累加兒子的cnt,因為如果fail[v]=u,則u一定是v的子迴文串!
	}
} ;

看到這大家應該對迴文樹有所瞭解了吧?

下面是一些題目,感興趣的話可以做一下~

相關推薦

Palindromic Tree——處理一類問題的強力工具

今天我們來學習一個神奇的資料結構:Palindromic Tree。中譯過來就是——迴文樹。 那麼這個迴文樹有何功能? 假設我們有一個串S,S下標從0開始,則迴文樹能做到如下幾點: 1.求串S字首0~

&& 求本質不同的和ACM-ICPC 2018 南京賽區網路預賽 I. Skr

Step1 Problem: 給你一個字串 s,求本質不同的迴文字串的加和 mod 1e9+7。 例:s = “1111”, ans = 1111 + 111 + 11 + 1. 資料範圍: 1 <= len <= 2e6.

線性基(處理集合異或的強力工具

看了好多篇關於線性基的部落格,只是說明了怎麼求線性基,但是大都沒有說明為什麼這樣求線性基。定義:有一個集合 S  = {a1,a2...,an},T的滿足下面條件的一個最小子集A = {a1,a2,....,ak}A的所有子集的異或和的值域與T的所有子集的異或和的值域相同,那

最好用的應用快速啟動工具Alfred 3.7.2 for Mac Powerpack

導入 高度 改進 結果 問題 文件同步 文件 play 優秀 【簡介】 今天和大家分享最新的 Alfred for Mac 3.7.2 版本,這是一款Mac上必備的高效率的應用快速啟動工具,Alfred 要比系統自帶的Spotlight功能更加強大

分享一款一直在維護的網路開發運維|通用除錯工具: http請求, websocket,cmd, RSA,DES, 引數簽名工具,指令碼批量生成工具,google動態口令,埠檢測,元件註冊,js混淆...

首先發下下載地址:https://files.cnblogs.com/files/taohuadaozhu/ConfigLab.Test.ex.rar      日常開發,運維,跨部門跨公司對接中。  想快速除錯,驗證一些介面,環境時需要快速處理的工具。本文章就分享這樣

「學習筆記」/自動機(Palindromic Tree)

引入 有時候題目要求一些這樣的問題 1. 求以串 s s s 本質不同的迴文串個數(即長度不同或長度相同且至少有一個字元不相同的字串) 2. 求以位置 i

Palindromic Tree 自動機- 解決的神器

迴文樹,也叫回文自動機,是2014年夏天戰鬥民族發明的,其功能如下: 1、求字首字串中的本質不同的迴文串種類 2、求每個本質不同迴文串的個數 3、以下標i為結尾的迴文串個數/種類 4、每個本質不同迴文串包含的本質不同迴文串種類 (本文參考自Palindromic Tree—

Palindromic Tree自動機-回 例題+講解

數組指針 post article 技術分享 ret tail 模板 分享 tree ---恢復內容開始--- 回文樹,也叫回文自動機,是2014年被西伯利亞民族發明的,其功能如下: 1、求前綴字符串中的本質不同的回文串種類 2、求每個本質不同回文串的個數 3、以下

&& 求每個出現次數HYSBZ

Step1 Problem: 給你一個字串 s,求迴文子串長度 * 該回文串子出現次數的最大值。 Step2 Ideas: 結論:一個字串 s,其長度為 len,該字串本質不同的迴文子串個數不會超過 len. 感性理解一下:增加一個字元

動態規劃 13LeetCode 647. Palindromic Substrings

LeetCode 647. Palindromic Substrings Solution1:我的答案 動態規劃,易解 class Solution { public: int count

APIO2014 jzoj 3654/洛谷 3649/bzoj 3676 (自動機)

題目 考慮一個只包含小寫拉丁字母的符串 s。我們定義 s的一個子串 t的“出現值”為 t在 s中的出現次數乘以t的長度。 請你求出s的所有迴文子串中的最大出現值。 分析 迴文樹(迴文自動機)模板題 迴文樹連結———連結 還有後綴自動機的寫法,但是我

HackerRankFunctional Palindromes(+字尾陣列+lcp排序+字串雜湊+二分)

這個頁面抓不太好,大家點進去看吧~~ 做過的用到資料結構+演算法最多的一個題……真真是做ACM以來做的最最麻煩的一個題…… 說白了其實就是板子大雜燴……但是會吐的那種。。 此外……此題價值75$……不要問我為什麼。。。TOT 現在進入正片—— 給你

清華集訓2017模擬12.10+鏈剖分)

Description: NYG 很喜歡研究迴文串問題,有一天他想到了這樣一個問題: 給出一個字串 S,現在有 4 種操作: • addl c :在當前字串的左端加入字元 c; • addr c :在當前字串的右端加入字元 c; • transl l

LeetCode-面試算法經典-Java實現107-Binary Tree Level Order Traversal II(二叉層序遍歷II)

lin -m length ret itl pub util 實現類 markdown 【107-Binary Tree Level Order Traversal II(二叉樹層序遍歷II)】 【LeetCode-面試算法經典-Java實現】【全

LeetCode-面試算法經典-Java實現106-Construct Binary Tree from Inorder and Postorder Traversal(構造二叉II)

struct ons node dcl 實現 ftl rsa tor var 【106-Construct Binary Tree from Inorder and Postorder Traversal(通過中序和後序遍歷構造二叉樹)】 【Lee

總結spark按本格式和Lzo格式處理Lzo壓縮件的比較

spark lzotextinputformat1、描述spark中怎麽加載lzo壓縮格式的文件2、比較lzo格式文件以textFile方式和LzoTextInputFormat方式計算數據,Running Tasks個數的影響 a.確保lzo文件所在文件夾中生成lzo.index索引文件 b.以

luoguP3690 模板Link Cut Tree (動態)[LCT]

格式 %d getch logs cstring name flag -1 處理 題目背景 動態樹 題目描述 給定N個點以及每個點的權值,要你處理接下來的M個操作。操作有4種。操作從0到3編號。點從1到N編號。 0:後接兩個整數(x,y),代表詢問從x到y的路徑上的

BZOJ2870最長道路tree 點分治+狀數組

soft amp 64位 路徑 最小值 tree names out turn 【BZOJ2870】最長道路tree Description H城很大,有N個路口(從1到N編號),路口之間有N-1邊,使得任意兩個路口都能互相到達,這些道路的長度我們視作一樣。每個路口

UVA536 Tree Recovery(型結構基礎)

cnblogs using include http tree c++ code div str 題目 題目 ? ? 分析 莫名A了 ? ? 代碼 #include <bits/stdc++.h> using namespace std; string s1

Spark MLlib速成寶典模型篇05決策Decision Tree(Python版)

back filter oms sse mlu eval ffffff size red 目錄   決策樹原理   決策樹代碼(Spark Python) 決策樹原理   詳見博文:http://www.cnblogs.com/itmorn/p/79