國際慣例的題面:
考慮我們求解出字串uvu第乙個u的右端點為i,第二個u的右端點為j,我們需要滿足什麼性質?
顯然j>i+l,因為我們選擇的串不能是空串。
另外考慮i和j的最長公共字首(也就是說其parent樹上lca的len),為了保證他們相同,我們需要:
j-len>=i-l。
整理一下,如果我們已知i,j需要在區間[i+l+1,i+l+len]中。
如果我們已知j,i需要在區間[j-l-len,j-l-1]中。
於是我們可以寫n^2暴力了:暴力維護parent上每個節點的right集合,對於每個i,暴力向上跳,暴力找可行的j。
然後我們發現,維護right集合可以用主席樹啟發式合併做,對於計算貢獻,我們可以先列舉lca,然後計算有多少組可行的i,j。
在第二個計算的時候,我們顯然是會在兩顆主席樹進行合併的時候進行計算,於是我們可以把較小的那顆拍扁,在另外一棵裡暴力查詢每乙個值的貢獻。
這樣總複雜度o(nlog^2n),輕鬆ac。
注意主席樹啟發式合併的時間和空間複雜度都是o(nlog^n)的,因為考慮每層攤還下來只會被新建logn次(我已經把長度和節點個數乘起來了)。
另外這題字符集大小為全體可見字元,所以需要用map存字尾自動機。
(不是很明白為什麼網上那麼多題解都是字尾陣列的,明明字尾自動機這麼好寫(不會字尾陣列的就不要說話了.jpg))
**:
1 #include2 #include3 #include4 #include5view codeusing
namespace
std;
6const
int maxn=1e5+1e2,maxl=18;7
8char
in[maxn>>1];9
intli,lim;
10int seq[maxn>>1
],seqlen;
11long
long
ans;
1213
struct
persistentsegmenttree
22 inline int merge(int p1,int p2,int l,int
r) 31 inline int query(int pos,int l,int r,const
int &ll,const
int &rr)
39 inline void dfs(int pos,int l,int
r) 45 inline int getsiz(int
pos)
48}segt;
4950
namespace
sam
58 inline int extend(int x,int
rr) 72}
73return last =np;74}
75 inline int query(int root,int i,int
samelen)
81 inline void
topo()
93if( segt.getsiz(roots[fa[pos]]) < segt.getsiz(roots[pos]) ) //
we won't use roots[pos] again .
94swap(roots[fa[pos]],roots[pos]);
95 seqlen = 0 , segt.dfs(roots[pos],1
,li);
96for(int i=1;i<=seqlen;i++) ans +=query(roots[fa[pos]],seq[i],len[fa[pos]]);
97 roots[fa[pos]] = segt.merge(roots[fa[pos]],roots[pos],1
,li);
98if( !--deg[fa[pos]] ) q.push(fa[pos]);99}
100}
101}
102103
intmain()
bzoj3998 字尾自動機
對於乙個給定長度為n的字串,求它的第k小子串是什麼。第一行是乙個僅由小寫英文本母構成的字串s 第二行為兩個整數t和k,t為0則表示不同位置的相同子串算作乙個。t 1則表示不同位置的相同子串算作多個。k的意義如題所述。輸出僅一行,為乙個數字串,為第k小的子串。如果子串數目不足k個,則輸出 1 aabc...
BZOJ 2780 字尾自動機
由於是英文題,簡要解釋一下題目。開始給出n個子串,和m個詢問,對於每個詢問讀入乙個子串,詢問n個子串中,有多少個子串包含所詢問的子串。實在看不懂的可以從樣例中意會一下。include include include include include include using namespace st...
BZOJ 4516 字尾自動機
題意 統計本質不同的子串數量。題解 模板題,字尾自動機。在每乙個節點處,統計一下他能代表多少個 以他為終點的串。然後動態建立sam,同時每加一次都輸出一次就行了。唯一的不同是,這個題是int,而不是傳統的字串,那麼把nxt陣列換成map,複雜度多乙個log sizeof nxt 實際上非常小。cod...