1. 程式人生 > >【SAM+線段樹合併】LGP4770 [NOI2018]你的名字

【SAM+線段樹合併】LGP4770 [NOI2018]你的名字

【題目】
原題地址
給定一個字串 S S ,多組詢問給定字串 T T 以及兩個數字 l ,

r l,r 。求 S [ l . . r ]
S[l..r]
中有多少個子串 x x 滿足: x x 的任意一個子串沒有在 T
T
中出現過。 S , T 5 × 1 0 5 |S|,\sum |T| \leq 5\times 10^5

【解題思路】
加深對 SAM \text{SAM} 的理解。

既然是字串題,我們首先對 S S T T 分別建 SAM \text{SAM}
考慮 l = 1 , r = S l=1,r=|S| 的情況。
l i m i lim_i T [ 1 , i ] T[1,i] 能匹配 S S 的最長字尾為 T [ i l i m i + 1 , i ] T[i-lim_i+1,i] (若 l i m i = 0 lim_i=0 T i T_i 沒有在 S S 中出現過)。那麼這個我們在 SAM \text{SAM} 上一路往下跑,匹配不了就往 f a fa 跳,這樣就可以簡單處理出來,有點類似雙指標。
SAM \text{SAM} 中節點 i i r i g h t right 集合包含的字串最大長度為 m x i mx_i ,字串第一次出現的位置為 e n d i end_i (這個節點 r i g h t right 集中的每個串 e n d i end_i 都是一樣的,因為是字尾包含關係,終點等價)。我們列舉 T T 中的每個節點,考慮每條 i i f a i fa_i 邊的貢獻,那麼:
a n s = i = 2 c n t m a x ( 0 , m x i m a x ( m x f a i , l i m e n d i ) ) ans=\sum_{i=2}^{cnt} max(0,mx_i-max(mx_{fa_i},lim_{end_i}))
這個式子的意思就是對於每個節點,不屬於 S S 的子串的總個數為當前節點所代表的集合字串個數減去與 S S 有匹配的字串個數。

於是現在實際上總的問題就是求這個 l i m i lim_i
我們可以對於 S S SAM \text{SAM} 每個節點按 m x i mx_i 排序,每個節點建線段樹表示出現位置,做線段樹合併。那麼每次我們求 l i m i lim_i ,往下擴充套件的時候,我麼只需要判斷這個節點線段樹中 [ l + l e n , r ] [l+len,r] 是否出現過即可。其中 l e n len 表示當前匹配了 T [ i l e n + 1 , i ] T[i-len+1,i] ,出現了即表示字串出現在 S [ l , r ] S[l,r] 內。

複雜度 O ( P log P ) O(P\log P) ,我們取 P P 1 0 6 10^6 級別吧。

【參考程式碼】

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1e6+10,M=N*20;
int rt[N],b[N],c[N],lim[N];
char s[N];
ll ans;

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

struct Segment
{
	int sz,ls[M],rs[M];
	void insert(int &x,int l,int r,int p)
	{
		if(!x) x=++sz;
		if(l==r) return;
		int mid=(l+r)>>1;
		if(p<=mid) insert(ls[x],l,mid,p);
		else insert(rs[x],mid+1,r,p);
	}
	int merge(int x,int y)
	{
		if(!x || !y) return x+y;
		int z=++sz;
		ls[z]=merge(ls[x],ls[y]);rs[z]=merge(rs[x],rs[y]);
		return z;
	}
	bool query(int x,int l,int r,int L,int R)
	{
		if(!x) return 0;
		if(L<=l && r<=R) return 1;
		int mid=(l+r)>>1;bool res=0;
		if(L<=mid) res|=query(ls[x],l,mid,L,R);
		if(R>mid) res|=query(rs[x],mid+1,r,L,R);
		return res;
	}
}tr;

struct SAM
{
	int sz,tot,las,n,mx[N],fa[N],pos[N],ch[N][26];
	void extend(int x,int c)
	{
		int p