1. 程式人生 > >[BZOJ5417/UOJ#395/NOI2018]你的名字(字尾自動機+主席樹)

[BZOJ5417/UOJ#395/NOI2018]你的名字(字尾自動機+主席樹)

Address

Solution…

68pts:l=1,r=Sl=1,r=|S|

建議先做:[BZOJ4566][Haoi2016]找相同字元
詢問前先建出 SS 的字尾自動機,然後求出 TT 的每個字首 T[1...i]T[1...i] 的字尾最多能在 SS 中匹配多長。也就是說,我們需要對於每個 ii 求出一個最大的 mx[i]=jmx[i]=j ,使得 T[ij+1...i]T[i-j+1...i]SS 中出現過。那麼顯然這樣對答案的貢獻為 iji-jjj 可以用在後綴自動機上走轉移邊和 Parent 邊得到,類似於求最長公共子串的方法。
但我們要統計的是 T

T 本質不同的子串,所以我們想辦法讓已經出現過的子串不被統計。所以建出 TT 的字尾自動機。同樣地,可以「自己和自己匹配」,求出一個最大的 nxt[i]=jnxt[i]=j 滿足 T[ij+1...i]T[i-j+1...i]T[1...i1]T[1...i-1] 的子串,那麼這樣左端點在 [nxt[i],i][nxt[i],i] 而右端點為 ii 的子串不會被再次統計。
那麼這次詢問的答案為:
i=1T{imax(nxt[i],mx[i])}\sum_{i=1}^{|T|}\{i-\max(nxt[i],mx[i])\}

複雜度 O(S+T)O(|S|+\sum|T|) 。期望得分 6868 分。

100pts:1lrS1\le l\le r\le |S|

nxt[i]nxt[i] 還是一樣。而關鍵在於求 mx[i]mx[i]
假設我們已經求得了 mx[i1]mx[i-1] 並且走到了 SS 的字尾自動機上的節點 uu
先考慮一個小問題:
如何判斷狀態 uu (不是初始狀態)所能夠表示出的所有長度為 kk 的子串中,是否存在一個出現在 S

[l...r]S[l...r] 內。
問題等價於求 uuRightRight 集合中是否存在一個數在 [l+k1,r][l+k-1,r] 範圍內。
我們知道,如果在構建 SS 的過程中將拆解出的所有點設為白點,其他點(初始狀態除外)為黑點,那麼點 uuRightRight 集合就是 uu 的 Parent 子樹內所有黑點。
於是轉化為求 uu 的 Parent 子樹中是否存在一個黑點的 MaxlMaxl[l+k1,r][l+k-1,r] 範圍內。
求出 Parent 樹的 DFS 序列之後,可以用主席樹求得。
回到原問題。考慮如何判斷狀態 uu 表示出的所有 SS 的子串是否存在一個子串包含於 [l,r][l,r] 。如果沒有則需要把 uu 往 Parent 跳直到存在或者跳回初始狀態為止。
易得,我們要求的是 uu 的 Parent 子樹內, MaxlMaxl[l,r][l,r] 範圍內並且最大的值。
仍然可以使用主席樹查這個值。
這樣我們就能得到狀態 uuS[l...r]S[l...r] 內能夠匹配的最長子串。
(注:黑點的 MaxlMaxl 的另一個含義是:這個點表示出的子串之一為 SS 的長度為 MaxlMaxl 的字首。故 uuRightRight 集合實際上是 uu 的 Parent 子樹內所有黑點的 MaxlMaxl 組成的集合)
具體實現:求得 mx[i1]mx[i-1] 以及到達的 SS 中的點 uu ,設 c=T[i]c=T[i]
如果以下兩條件都滿足
(1) uu 有字元 cc 的轉移邊。設 uu 通過字元 cc 轉到 vv
(2) vvS[l...r]S[l...r] 內能匹配到子串,設最長長度為 ll
就將 uu 通過字元 cc 邊轉移,並把 mx[i]mx[i] 設成 ll
否則將 uu 跳到 Parent 繼續判斷。
襠燃,如果 uu 跳到了根就直接退出。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define SAM_AS(i, z) for (; i && z; i = AS[i].fa)
#define SAM_AT(i, z) for (; i && z; i = AT[i].fa)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
using namespace std;

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

template <class T>
T Max(T a, T b) {return a > b ? a : b;}

template <class T>
T Min(T a, T b) {return a < b ? a : b;}

typedef long long ll;
const int N = 5e5 + 5, M = N << 1, L = 3e7 + 5;

int n, m, q, lst, ToT, QAQ, w[N], tmp[M], ecnt, nxt[M], adj[M], go[M],
rt[M], QwQ, dfn[M], sze[M], TAT, a[M];
char S[N], T[N];

void add_edge(int u, int v)
{
	nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
}

struct node
{
	int fa, go[26], maxl, ri;
	void init()
	{
		fa = maxl = ri = 0;
		memset(go, 0, sizeof(go));
	}
} AS[M], AT[M];

struct seg
{
	int lc, rc, sum;
} Tr[L];

void ins(int y, int &x, int l, int r, int p)
{
	Tr[x = ++TAT] = Tr[y]; Tr[x].sum++;
	if (l == r) return;
	int mid = l + r >> 1;
	if (p <= mid) ins(Tr[y].lc, Tr[x].lc, l, mid, p);
	else ins(Tr[y].rc, Tr[x].rc, mid + 1, r, p);
}

int querysum(int y, int x, int l, int r, int up)
{
	if (l == r) return Tr[x].sum - Tr[y].sum;
	int mid = l + r >> 1;
	if (up <= mid) return querysum(Tr[y].lc, Tr[x].lc, l, mid, up);
	else return Tr[Tr[x].lc].sum - Tr[Tr[y].lc].sum
		+ querysum(Tr[y].rc, Tr[x].rc, mid + 1, r, up);
}

int squery(int y, int x, int l, int r, int sum)
{
	if (l == r) return l;
	int mid = l + r >> 1, delta = Tr[Tr[x].lc].sum - Tr[Tr[y].lc].sum;
	if (sum <= delta) return squery(Tr[y].lc, Tr[x].lc, l, mid, sum);
	else return squery(Tr[y].rc, Tr[x].rc, mid + 1, r, sum - delta);
}

int querypre(int l, int r, int tl, int tr)
{
	int tmp = querysum(rt[l - 1], rt[r], 0, n, tr);
	if (!tmp) return 0;
	int rev = squery(rt[l - 1], rt[r], 0, n, tmp);
	return tl <= rev ? rev : 0;
}

void extendS(int x)
{
	int i = lst, c = S[x] - 'a';
	AS[lst = ++ToT].init();
	AS[lst].ri = AS[lst]
            
           

相關推薦

[BZOJ5417/UOJ#395/NOI2018]名字字尾自動機+主席

Address Solution… 68pts:l=1,r=∣S∣l=1,r=|S|l=1,r=∣S∣ 建議先做:[BZOJ4566][Haoi2016]找相同字元 詢問前先建出 SSS 的字尾自動機,然後求出 TTT 的每個字首 T[1...i]T[1...

Luogu4770 NOI2018名字字尾陣列+線段

  即求b串有多少個本質不同的非空子串,在a串的給定區間內未出現。即使已經8102年並且馬上就9102年了,還是要高舉SA偉大旗幟不動搖。   考慮離線,將所有詢問串及一開始給的串加分隔符連起來,求出SA。對於每個詢問,我們對串的每個字尾,求出其在給定區間中最長的lcp是多少。這樣就能得到不考慮本質不同時的

bzoj千題計劃319:bzoj2865: 字串識別字尾自動機 + 線段

#include<map> #include<cstdio> #include<cstring> #include<algorithm> #define N 500001 using namespace std; char s[

bzoj千題計劃318:bzoj1396: 識別子串字尾自動機 + 線段

#include<cstdio> #include<cstring> #include<algorithm> #define N 100001 using namespace std; char s[N]; int ch[N<&

bzoj 1396: 識別子串 字尾自動機+線段

1396: 識別子串 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 308  Solved: 190 [Submit][Status][Discuss] Description Input 一行,一個由小寫字母組成

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

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

BZOJ5417名字NOI2018-字尾自動機+主席

測試地址:你的名字 做法:本題需要用到字尾自動機+主席樹。 首先考慮l=1,r=|S|l=1,r=|S|的情況。考慮TT的每個字首的貢獻,我們需要找到它最短的沒在SS中出現過,而且沒在TT的前面部分出現過的字尾,這樣包含它的所有後綴就都是合法的貢獻了。顯然這

[UOJ#395][NOI2018]名字

Description 給出字串S,每次詢問字串T有多少本質不同的子串不是S[l…r]的子串 |S|,|T|<=10^5 Solution 我太菜了考場68分都寫掛了_ (:з」∠) _ 其實這道題並沒有看上去那麼難 預處理出lim[i]表示T的字首i的最

【男人八題】 A.String Game字尾自動機 + sg函式

題目大意:給一個模式串和n個它的子串,Alice和Bob玩遊戲,Alice先手,每回合任選一個子串,該回合輪到的人在它後面加一個字母,並且保證加了之後的新串仍然是模式串的子串。輪到後沒辦法保證上述新增要求的人輸。 (雖然題目沒有說,但是字符集是小寫字母) 可以

P5161 WD與數列字尾自動機+線段合併

傳送門 沒想出來→_→ 首先不難看出要差分之後計算不相交也不相鄰的相等子串對數,於是差分之後建SAM,在parent樹上用線段樹合併維護endpos集合,然後用啟發式合併維護一個節點對另一個節點的貢獻,於是總的時間複雜度為\(O(n\log^2n)\) //minamoto #include<bit

CodeChef February Challenge 2018 Chef and odd queries 分塊 + 主席

turn () += -- oid sca com print const 題目鏈接 Chef and odd queries 題意 給定$n$個區間和$q$個詢問,每個詢問給定$m$個點,求這$n$個區間中有多少個包含了$m$個點中的奇數個。 分類操作。

p3168 [CQOI2015]任務查詢系統差分+主席

恕我才學淺薄,一開始想到的是樹狀陣列+線段樹,然後看了題解才第一次見到了差分這種神奇的科技 仔細想想,主席樹的本質不就是字首和嘛,加上一個差分也是可以的,沒想到真是罪過罪過 對時間維護一個差分 在Si處+Ki,在Ti+1處-Ki 用主席樹維護插入的數即可 不是很複雜就是程式碼寫了好長時間而且越debug越像題

[BZOJ2653]middle二分答案 + 主席

Address 洛谷 P2839 BZOJ 2653 Solution 很不錯的題 求某個排名的數的最值是一個經典的二分答案套路 方法為:二分答案 m

P2617 Dynamic Rankings帶修主席

scanf getc max tchar dynamic () cstring cpp ngs 所謂帶修主席樹,就是用樹狀數組的方法維護主席樹的前綴和 思路 帶修主席樹的板子 註意數據範圍顯然要離散化即可 代碼 #include <cstdio> #incl

【BZOJ3123】[SDOI2013] 森林啟發式合併主席

點此看題面 大致題意: 給你一片森林,有兩種操作:詢問兩點之間的第kkk小點權和在兩棵樹之間連一條邊。 前置技能:樹上主席樹 做這道題目,我們首先要會樹上主席樹。 關於樹上主席樹,這有一道很好的例題:【洛谷2633】Count on a tree(只包含此題的

HDU 5069 Harry And Biological TeacherAC自動機+線段

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

LOJ 2720 & UOJ 395 & BZOJ 5417 「NOI2018名字 後綴自動機 線段合並

class digi spa write .so 節點 isa 字符 ring LOJ #2720 UOJ #395 BZOJ 5417 題意 給出字符串S,有Q次詢問,每次給出字符串T和整數\(l,r\)滿足\(1\le l\le r\le|S|\) 求T有多少個本質不

BZOJ.5417.[NOI2018]名字(字尾自動機 線段合併)

LOJ 洛谷 BZOJ 考慮\(l=1,r=|S|\)的情況: 對\(S\)串建SAM,\(T\)在上面匹配,可以得到每個位置\(i\)的字尾的最長匹配長度\(mx[i]\)。 因為要去重,對\(T\)也建SAM,計算上面所有節點的答案。記\(pos[i]\)表示\(i\)節點第一次出現的下標(同一節點代表

bzoj5417&&luogu4770名字 字尾自動機+線段合併

bzoj5417&&luogu4770你的名字 題目傳送門: 洛谷 bzoj 分析 題目大意: 給定一個模板串 S

BZOJ5417[Noi2018]名字——後綴自動機+線段合並

else while pre 所有 http class () pan 維護 題目鏈接: [Noi2018]你的名字 題目大意:給出一個字符串$S$及$q$次詢問,每次詢問一個字符串$T$有多少本質不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$