bzoj3238 差異(字尾陣列 單調棧)

2022-05-12 16:43:07 字數 3015 閱讀 6308

顯然我們可以先把len(ti)+len(tj)的值先算出來,再把lcp減去。所有len(ti)+len(tj)的值為n*(n-1)*(n+1)/2,這個隨便在紙上畫一畫就可以算出來的。

接下來問題就是如何把lcp減去。我們先用字尾陣列把height求出來,當有一段區間l~r,height[i]為height[l]~height[r]中的最小值,那麼隨便取rk[l]~rk[r]中的兩個字尾,他們的lcp則都是height[i],這個很好理解吧。那麼l~r這個區間裡有(l-i+1)*(r-i+1)對字尾,所以我們最後的答案就要減去2*height[i]*(l-i+1)*(r-i+1)【1≤i≤n】。

然後就是如何求出每乙個i的l~r了,暴力列舉+rmq顯然不行,那我們就用乙個單調棧,棧裡存著i前面height值比height[i]小的height值的編號,記為j,如果height[j]比height[i]大那麼就彈出,那麼這段區間的左端點則為棧頂的j+1,右端點同理。這樣我們就可以求出每個height的l和r了。

奇醜無比的**如下:

var

s:ansistring;

i:longint;

n,m,l,r,ans,top:int64;

rk,trk,sa,tsa,sum,h,ll,rr,st:

array[0..500005]of

int64;

procedure

suffix;

vari,j,p:longint;

begin

for i:=1

to n do

begin trk[i]:=ord(s[i]);inc(sum[trk[i]]);end;

for i:=2

to255

do inc(sum[i],sum[i-1

]);

for i:=n downto1do

begin sa[sum[trk[i]]]:=i;dec(sum[trk[i]]);end

; rk[sa[

1]]:=1;p:=1;

for i:=2

to n do

begin

if trk[sa[i]]<>trk[sa[i-1]] then inc(p);rk[sa[i]]:=p;end

; m:=p;j:=1;

while mdo

begin

move(rk,trk,sizeof(rk));fillchar(sum,sizeof(sum),

0);p:=0

;

for i:=n-j+1

to n do

begin inc(p);tsa[p]:=i;end

;

for i:=1

to n do

if sa[i]>j then

begin inc(p);tsa[p]:=sa[i]-j;end

;

for i:=1

to n do

begin rk[i]:=trk[tsa[i]];inc(sum[rk[i]]);end

;

for i:=2

to n do inc(sum[i],sum[i-1

]);

for i:=n downto1do

begin sa[sum[rk[i]]]:=tsa[i];dec(sum[rk[i]]);end

; rk[sa[

1]]:=1;p:=1

;

for i:=2

to n do

begin

if (trk[sa[i]]<>trk[sa[i-1]])or(trk[sa[i]+j]<>trk[sa[i-1]+j])then

inc(p);

rk[sa[i]]:=p;

end;

m:=p;j:=j*2;

end;

h[1]:=0;p:=0;

for i:=1

to n do

begin

if rk[i]=1

then

continue;

j:=sa[rk[i]-1

];

while s[i+p]=s[j+p] do

inc(p);

h[rk[i]]:=p;

if p>0

then

dec(p);

end;end

;begin

readln(s);

n:=length(s);

s:=s+'';

suffix;

ans:=n*(n-1)*(n+1)div2;

h[0]:=-maxlongint;

for i:=1

to n do

begin

while h[i]<=h[st[top]] do

dec(top);

if st[top]=0

then ll[i]:=1

else ll[i]:=st[top]+1

; inc(top);

st[top]:=i;

end; h[n+1]:=-maxlongint;top:=0;st[0]:=n+1;

for i:=n downto0do

begin

while h[i]do

dec(top);

if st[top]=n+1

then rr[i]:=n

else rr[i]:=st[top]-1

; inc(top);

st[top]:=i;

end;

for i:=1

to n do

ans:=ans-2*(i-ll[i]+1)*(rr[i]-i+1)*h[i];

writeln(ans);

end.

view code

bzoj3238 差異 字尾樹

題目大意 給你乙個字串 s 設 s i 是串 s 第 i 長的字尾,求 sum limits sum limits s i s j 2 times lcp s i,s j 其中 lcp x,y 表示字串 x 和字串 y 的最長公共字首 資料範圍 s 500000 最近發現字尾樹和 sam 沒學好,找...

BZOJ3238 差異(字尾自動機)

bzoj 前面的東西直接暴力算就行了 其實沒必要算的正正好 為了方便的後面的計算 我們不考慮i,j 的順序問題 也就是先求出 n i 1 nj 1 i j len i 然後對於每個字尾樹上的節點,減去一下貢獻 也就是siz e i si ze i 1 le n i len i.p aren t 這樣...

BZOJ3238 差異(字尾自動機)

bzoj 前面的東西直接暴力算就行了 其實沒必要算的正正好 為了方便的後面的計算 我們不考慮 i,j 的順序問題 也就是先求出 sum n sum n i neq j len i 然後對於每個字尾樹上的節點,減去一下貢獻 也就是 size i size i 1 len i len i.parent ...