1. 程式人生 > >關於維護AC自動機fail樹的三道題目

關於維護AC自動機fail樹的三道題目

由易到難

GRE Words hdu 4117 

這題本來的做法是ac自動機優化dp,然而hdu加入了新的資料,估計構造了一些會導致fail鏈很長的資料,以前的做法會TLE,這就又需要我們維護fail樹,用線段樹維護fail樹的dfs序列,支援單點修改區間求最值。需要注意的是當fail鏈比較短時(比如這題的原始資料),用線段樹維護做法反而耗時更長。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstring>
#include <stack>
#include <cctype>
#include <utility>   
#include <map>
#include <string>  
#include <climits> 
#include <set>
#include <string>    
#include <sstream> 
#include <ctime>
#include <bitset>
#include <iomanip>
#pragma comment(linker, "/STACK:102400000,102400000")

using std::priority_queue;
using std::vector;
using std::swap;
using std::stack;
using std::sort;
using std::max;
using std::min;
using std::pair;
using std::map;
using std::string;
using std::cin;
using std::cout;
using std::set;
using std::queue;
using std::string;
using std::stringstream;
using std::make_pair;
using std::getline;
using std::greater;
using std::endl;
using std::multimap;
using std::deque;
using std::unique;
using std::lower_bound;
using std::random_shuffle;
using std::bitset;
using std::upper_bound;
using std::multiset;
using std::ios;
using std::make_heap;
using std::push_heap;
using std::pop_heap;

typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned UN;
typedef pair<int, int> PAIR;
typedef multimap<int, int> MMAP;
typedef long double LF;

const int MAXN(300010);
const int MAXM(20010);
const int MAXE(2100010);
const int MAXK(6);
const int HSIZE(1313131);
const int SIGMA_SIZE(26);
const int MAXH(18);
const int INFI((INT_MAX-1) >> 1);
const ULL BASE(31);
const LL LIM(1e13);
const int INV(-10000);
const int MOD(1000000007);
const double EPS(1e-7);
const LF PI(acos(-1.0));

template<typename T> inline bool checkmax(T &a, T b){if(b > a) { a = b; return true;} return false;}
template<typename T> inline bool checkmin(T &a, T b){if(b < a) { a = b; return true;} return false;}
template<typename T> inline T ABS(T a){return a < 0? -a: a;}
template<typename T> inline bool EZ(T a){return ABS(a) < EPS;}

struct SGT{
	int val[MAXN << 2];
	inline int ls(int rt){return rt << 1;}
	inline int rs(int rt){return (rt << 1)|1;}
	inline void push_up(int rt){val[rt] = max(val[ls(rt)], val[rs(rt)]);}
	void build(int l, int r, int rt){
		val[rt] = -INFI;
		if(l == r) return;
		int m = (l+r) >> 1;
		build(l, m, ls(rt));
		build(m+1, r, rs(rt));
	}
	void update(int l, int r, int rt, int qi, int qv){
		if(l == r){
			checkmax(val[rt], qv);
			return;
		}
//		push_down(rt);
		int m = (l+r) >> 1;
		if(qi <= m) update(l, m, ls(rt), qi, qv);
		else update(m+1, r, rs(rt), qi, qv);
		push_up(rt);
	}
	int query(int l, int r, int rt, int ql, int qr){
		if(ql <= l && qr >= r) return val[rt];
//		push_down(rt);
		int m = (l+r) >> 1;
		int ret = -INFI;
		if(ql <= m) checkmax(ret, query(l, m, ls(rt), ql, qr));
		if(qr > m) checkmax(ret, query(m+1, r, rs(rt), ql, qr));
		return ret;
	}
} sgt;

int left[MAXN], right[MAXN], ind;

struct TREE{
	int *first, *v, *next;  //記憶體優化,先借用線段樹的記憶體
	int rear;
	void init(int n){
		first = sgt.val;
		v = sgt.val+MAXN;
		next = sgt.val+2*MAXN;
		memset(first, -1, sizeof(first[0])*n);
		rear = 0;
	}
	void insert(int tu, int tv){
		v[rear] = tv;
		next[rear] = first[tu];
		first[tu] = rear++;
	}
	void dfs(int u){
		left[u] = ++ind;
		for(int i = first[u]; ~i; i = next[i])
			dfs(v[i]);
		right[u] = ind;
	}
} tree;

int po[MAXM], sco[MAXM];
int front, back;
struct AC{
	int f[MAXN], ch[MAXN][SIGMA_SIZE], size;
	void init(){
		memset(ch[0], 0, sizeof(ch[0])); // !!!memset(ch[0], 0, sizeof(ch));
		size = 1;
	}
	void insert(char *sp, int i){
		int rt = 0;
		for(; *sp; ++sp){
			int id = *sp-'a';
			if(!ch[rt][id]){
				memset(ch[size], 0, sizeof(ch[size]));
				ch[rt][id] = size++;
			}
			rt = ch[rt][id];
		}
		po[i] = rt;
	}
	void construct(){
		front = back = 0;
		tree.init(size);
		for(int i = 0; i < SIGMA_SIZE; ++i)
			if(ch[0][i]){
				f[ch[0][i]] = 0;
				tree.insert(0, ch[0][i]);
				left[back++] = ch[0][i]; 
			}
		while(front < back){
			int cur = left[front++];	
			for(int i = 0; i < SIGMA_SIZE; ++i){
				int u = ch[cur][i];
				if(u){
					f[u] = ch[f[cur]][i];
					tree.insert(f[u], u);
					left[back++] = u;
				} 
				else
					ch[cur][i] = ch[f[cur]][i];
			}
		}
		ind = 0;
		tree.dfs(0);
	}
} ac;

char str[MAXN+MAXM];
char *tst[MAXM];

int main(){
//	freopen("d:\\in.txt", "r", stdin);
//    freopen("d:\\out.txt", "w", stdout);
	int TC, n_case(0);
	scanf("%d", &TC);
	while(TC--){
		ac.init();
		int n;
		scanf("%d", &n);
		tst[0] = str;
		for(int i = 0; i < n; ++i){
			scanf("%s%d", tst[i], sco+i);
			tst[i+1] = tst[i]+strlen(tst[i])+1;
			ac.insert(tst[i], i);
		}
		ac.construct();
		sgt.build(1, ind, 1);
		int ans = 0;
		for(int i = n-1; i >= 0; --i){
			int temp = sgt.query(1, ind, 1, left[po[i]], right[po[i]]);
			checkmax(temp, 0);
			temp += sco[i];
			int rt = 0;
			for(char *sp = tst[i]; *sp; ++sp){
				int id = *sp-'a';
				rt = ac.ch[rt][id];
				sgt.update(1, ind, 1, left[rt], temp);
			}
			checkmax(ans, temp);
		}
		printf("Case #%d: %d\n", ++n_case, ans);
	}
	return 0;
}

阿狸的打字機

相關推薦

關於維護AC自動機fail題目

由易到難 GRE Words hdu 4117  這題本來的做法是ac自動機優化dp,然而hdu加入了新的資料,估計構造了一些會導致fail鏈很長的資料,以前的做法會TLE,這就又需要我們維護fail樹,用線段樹維護fail樹的dfs序列,支援單點修改區間求最值。需

洛谷P3966 [TJOI2013]單詞 單詞 (ac自動機 fail的應用)

emp c++ lse class 是我 文章 amp 就是 http 目錄 洛谷P3966 [TJOI2013]單詞 單詞 (ac自動機 fail樹的應用) 概述: 參考代碼 洛谷P3966 [TJOI2013]單詞 單詞 (ac自動機 fail樹的應用) 題目鏈接

AC自動機fail小結

建議大家學過AC自動機之後再來看這篇小結 fail樹就是講fail指標看做一條邊連成的樹形結構 fail指標在AC自動機中的含義是指以x為結尾的字尾在其他模式串中所能匹配的最長字首的長度 所以在模式串中一定有sq[1~fa[x]]為sk[1~x]的子串 這個性質可以解決一些問題: 單詞 顯然,我們

bzoj3881 [Coci2015]Divljak(AC自動機+fail+dfs序+狀陣列+鏈剖分)

bzoj3881 [Coci2015]Divljak 題意: Alice有n個字串S1,S2...SnS1,S2...Sn,Bob有一個字串集合T,一開始集合是空的。 接下來會發生q個操作,操作有兩種形式: “1 P”,Bob往自己的集合裡添加了一個

bzoj4231 回憶AC自動機+fail+KMP(+狀陣列))

bzoj4231 回憶樹 題意: 回憶樹是樹。 具體來說,是n個點n-1條邊的無向連通圖,點標號為1~n,每條邊上有一個字元(出於簡化目的,我們認為只有小寫字母)。 對一棵回憶樹來說,回憶當然是少不了的。 一次回憶是這樣的:你想起過往,觸及心底…唔

題目

string all pre ces one ace inpu rom different Q1. String to Integer implement a similar atoi function to convert a string to integer(int

HDU 5069 Harry And Biological Teacher(AC自動機+線段

題意 給定 \(n\) 個字串,\(m\) 個詢問,每次詢問 \(a\) 字串的字尾和 \(b\) 字串的字首最多能匹配多長。 \(1\leq n,m \leq 10^5\) 思路 多串匹配,考慮 \(\text{AC}\)自動機,對 \(n\) 個串建自動機,觀察這個結構,不難發現 \(Trie\)

NOI 2011 阿狸的打字機(AC自動機+主席

題意 https://loj.ac/problem/2444 思路 ​多串匹配,考慮 \(\text{AC}\) 自動機。模擬打字的過程,先建出一棵 \(\text{Trie}\) 樹,把它變成自動機。對於每一個詢問 \((x,y)\) ,相當於求 \(y\) 在 \(\text{Trie}\) 上的父

AC自動機 & fail

求一個 -a tex 深度 text 多少 上下 node 直接 [BZOJ 1195] 最短母串   題意   給定 n (n <= 12) 個子串 s (|s| <= 60) , 求一個最短母串.   分析   建 AC 自動機.   設 St 為每個點的狀

字串匹配的個演算法(KMP+字典+AC自動機

字串匹配的意思是給一個字串集合,和另一個字串集合,看這兩個集合交集是多少。 (1)若是都只有一個字串,那麼就看其中一個是否包含另外一個(一對一,KMP) https://blog.csdn.net/fkyyly/article/details/48007965 (2)若是父串集合(比較長

BZOJ2434 [NOI2011] 阿狸的打字機 【鏈剖分】【線段】【fail】【AC自動機

題目分析: 畫一下fail樹,就會發現就是x的子樹中屬於y路徑的,把y剖分一下,用線段樹處理 $O(n*log^2 n)$。 程式碼: 1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int ma

LA_3942 LA_4670 從字典AC自動機

while 原理 strlen sca lld 方式 下午 -- mod 首先看第一題,一道DP+字典樹的題目,具體中文題意和題解見訓練指南209頁。 初看這題模型還很難想,看過藍書提示之後發現,這實際上是一個標準DP題目:通過數組來儲存後綴節點的出現次數。也就是用一顆字典

【bzoj3881】[Coci2015]Divljak AC自動機+鏈的並+DFS序+狀數組

Go using %d 字符串表 rdquo alice microsoft fail 需要 題目描述 Alice有n個字符串S_1,S_2...S_n,Bob有一個字符串集合T,一開始集合是空的。 接下來會發生q個操作,操作有兩種形式: “1 P&rdq

【BZOJ】2434: [Noi2011]阿貍的打字機 AC自動機+狀數組+DFS序

log 字符串 html 有趣的 http .com dfs 個性 blog 【題意】阿貍喜歡收藏各種稀奇古怪的東西,最近他淘到一臺老式的打字機。打字機上只有28個按鍵,分別印有26個小寫英文字母和‘B‘、‘P‘兩個字母。 經阿貍研究發現,這個打字機是這樣工作的: l 輸入

【字符串】BZOJ上面幾個AC自動機求最為字串出現次數的題目

參考 none ac自動機 其他 view lose 細節 pen max (一下只供自己復習用,目的是對比這幾個題,所以寫得不詳細。需要細節的可以參考其他博主) 【BZOJ3172:單詞】 題目: 某人讀論文,一篇論文是由許多(N)單詞組成。但他發

BZOJ2434[Noi2011]阿貍的打字機——AC自動機+dfs序+狀數組

IE memset 換行 收藏 就是 namespace fail樹 src 輸入 題目描述 阿貍喜歡收藏各種稀奇古怪的東西,最近他淘到一臺老式的打字機。打字機上只有28個按鍵,分別印有26個小寫英文字母和‘B‘、‘P‘兩個字母。經阿貍研究發現,這個打字機是這樣工作

CodeForces 547E:Mike and Friends(AC自動機+DFS序+主席

src father ast bsp each In ant 如何 PE What-The-Fatherland is a strange country! All phone numbers there are strings consisting of lowercas

AC自動機】【字符串】【字典AC自動機 學習筆記

none ring mem ems 如何 top 暴力 繼續 編號 blog:www.wjyyy.top AC自動機是一種毒瘤的方便的多模式串匹配算法。基於字典樹,用到了類似KMP的思維。 AC自動機與KMP不同的是,AC自動機可以同時匹配多個模式串,而

NOI 2011 阿貍的打字機 (AC自動機+dfs序+狀數組)

amp span 離線處理 oid 描述 can include nbsp 不能 題目大意:略(太長了不好描述) 良心LOJ傳送門 先對所有被打印的字符串建一顆Trie樹 觀察數據範圍,並不能每次打印都從頭到尾暴力建樹,而是每遍歷到一個字符就在Trie上插入這個字符,然後記

【javascript實現】幾題目帶你學習二叉搜尋

二叉樹大家都知道,二叉搜尋樹滿足以下特徵: 節點的左子樹只包含小於當前節點的數 節點的右子樹只包含大於當前節點的數 所有左子樹和右子樹自身必須也是二叉搜尋樹 二叉搜尋樹也叫二叉排序樹,中序遍歷二叉搜尋樹的結果就是一次遞增的遍歷。 一、二叉搜尋樹的建立 相關題目:lee