AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=3238php
發現前面的len(Ti)+len(Tj)都是定值,因此其實咱們的目標就是求出lcp(Ti,Tj)來.ide
lcp說實話就是一段子串對吧,可是不太好找.因此咱們能夠把串反過來.spa
這樣的話,原圖中後綴的最長公共前綴就變成了新串中前綴的最長公共後綴.code
這樣的話提到了兩個概念:一是前綴,一是最長公共前綴blog
前綴的話就是每一個狀態的最長串啦...固然要除去一些是由本來的節點剖分開的節點.這樣的話一個節點僅僅只表示一個串.
排序
而最長公共後綴是十分好求的,咱們熟知parent邊就是一個不斷取後綴的過程,而後二者不斷刪去首字母,直到後綴相同的時候也就到了它們在parent樹上的LCA,LCA節點的最長長度就是它們的最長公共後綴.get
因此咱們要算前綴的公共後綴就是考慮在一棵parent樹上,考慮每一個LCA能夠是多少個點對的LCA.[這應該是一個比較基礎的樹型DP...]string
那麼其實只要知道LCA的全部兒子的right集合大小便可.(由於right集合的每一個位置分別表示了一段前綴[其實也就是這個節點下面的子樹大小],)it
而後與它父親的其它可能的前綴的可能相乘 就獲得了有多少個點對.io
而後怎麼求right集合的大小呢?
有一個性質就是parent樹是一個right集合合併的過程,那麼一個點的right集合大小就是其全部子樹right集合大小之和.
還記得以前有畫過一個圖,如今再扯過來講一下:
那麼咱們要是能把葉子節點的值賦值爲1,而後沿着parent往上走就能夠獲得了.
葉子節點怎麼找呢?其實很簡單...當你每次extend的時候,你會增長一個新的節點np,這個節點中就只包含一個元素{n},而後其它全部right集合中有n的都是它的祖先,因此它就是一個葉子節點,須要初始的時候就+1.
而有時候咱們將一個節點拆分紅兩個的時候,並無產生新的元素,因此不是葉子節點不須要+1.
固然有時候你不肯定的緣由是有的,好比:"abab"中的"ab"它明明有兒子,可是它確實又是一個葉子節點,提供了它本身right集合裏面的2這個元素.若是把串最開始的部分加一個"無"的話,就能夠看出來.
("ab")={2,4}--b-->("bab")={4}
--無-->("無ab")={2}
因此每一個前綴都必定在葉子節點上...好神奇啊...而後你們記住就行了.
固然還有一個樹剖要知足的拓撲序的完成,這個就是按照長度來弄一下順序就行了,越長的固然越在後面.具體的思路是一個聯繫桶排的思路,打過基數排序的同窗應該會很熟悉.
不懂的看代碼就好.
#include<cstdio> #include<cstring> #include<algorithm>
using namespace std; typedef long long ll; const int maxn=1000010; int n,last=1,cnt=1; int a[maxn][26],mx[maxn],fa[maxn]; int T[maxn],Seq[maxn]; int f[maxn],w[maxn]; char ch[maxn]; void extend(int c){ int p=last,np=last=++cnt; mx[np]=mx[p]+1; f[np]=w[np]=1; while(!a[p][c] && p) a[p][c]=np,p=fa[p]; if(!p) fa[np]=1; else{ int q=a[p][c]; if(mx[q]==mx[p]+1) fa[np]=q; else{ int nq=++cnt;mx[nq]=mx[p]+1; memcpy(a[nq],a[q],sizeof(a[q])); fa[nq]=fa[q]; fa[q]=fa[np]=nq; while(a[p][c]==q) a[p][c]=nq,p=fa[p]; } } } void get_order(){ for(int i=1;i<=cnt;i++) T[mx[i]]++; for(int i=1;i<=n;i++) T[i]+=T[i-1]; for(int i=1;i<=cnt;i++) Seq[T[mx[i]]--]=i; } ll solve(){ ll ans=0; for(int i=cnt;i>=1;i--) f[fa[Seq[i]]]+=f[Seq[i]]; for(int i=cnt;i>=1;i--){ int x=Seq[i]; ans+=(ll)f[x]*w[fa[x]]*mx[fa[x]]; w[fa[x]]+=f[x]; } return ans; } int main(){ #ifndef ONLINE_JUDGE freopen("3238.in","r",stdin); freopen("3238.out","w",stdout); #endif scanf("%s",ch); n=strlen(ch); for(int i=n-1;i>=0;i--) extend(ch[i]-'a'); ll ans=(ll)(1+n)*n*(n-1)/2; get_order(); ans-=solve()*2; printf("%lld",ans); return 0; }