日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > C# >内容正文

C#

NOI.AC#2144-子串【SAM,倍增】

發(fā)布時(shí)間:2023/12/3 C# 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NOI.AC#2144-子串【SAM,倍增】 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

正題

題目鏈接:http://noi.ac/problem/2144


題目大意

給出一個(gè)字符串sss和一個(gè)序列aaa。將字符串sss的所有本質(zhì)不同子串降序排序后,求有多少個(gè)區(qū)間[l,r][l,r][l,r]使得子串sl,rs_{l,r}sl,r?排名等于al~ra_{l\sim r}alr?的和。

1≤n≤2×1051\leq n\leq 2\times 10^51n2×105


解題思路

因?yàn)槭墙敌蚺判?#xff0c;所以每加一個(gè)字符排名是在下降的,而aia_iai?的和又是不降的,所以對(duì)于每個(gè)左端點(diǎn)最多只有一個(gè)右端點(diǎn),且可以考慮二分求出這個(gè)位置。

如何快速得到子串排名,開始不會(huì)還去LA\text{LA}LA群?jiǎn)柫艘幌虏胖馈?/p>

SAMSAMSAM的一個(gè)節(jié)點(diǎn)代表多個(gè)串,不能通過(guò)節(jié)點(diǎn)來(lái)得到排名。后綴樹上的一個(gè)節(jié)點(diǎn)也是代表多個(gè)串,但是這些串的排名是連續(xù)的(因?yàn)檫@些串都有相同的前綴)。

所以我們可以根據(jù)后綴樹上確定每個(gè)節(jié)點(diǎn)的最小排名,然后用倍增找出子串sl,rs_{l,r}sl,r?的節(jié)點(diǎn),再根據(jù)長(zhǎng)度確定具體排名。此時(shí)我們可以做到O(nlog?2n)O(n\log^2 n)O(nlog2n),可以通過(guò)本題了。

但還可以繼續(xù)優(yōu)化,發(fā)現(xiàn)我們倍增的過(guò)程有大量重復(fù),越往上排名越后,所以我們類似二分的判斷方法直接用倍增跳到答案節(jié)點(diǎn),然后在答案節(jié)點(diǎn)處再二分就好了。

時(shí)間復(fù)雜度O(nlog?n)O(n\log n)O(nlogn)


code

#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll N=4e5+10,T=20; ll n,cnt,num,last,tot,len[N],fa[N],ch[N][26],pos[N],id[N]; ll w[N],dep[N],f[N][T+1],t[N][26],rk[N],p1[N],p2[N]; char s[N]; void Insert(ll c){ll p=last,np=last=++cnt;len[np]=len[p]+1;for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;if(!p)fa[np]=1;else{ll q=ch[p][c];if(len[p]+1==len[q])fa[np]=q;else{ll nq=++cnt;len[nq]=len[p]+1;pos[nq]=pos[q];memcpy(ch[nq],ch[q],sizeof(ch[nq]));fa[nq]=fa[q];fa[q]=fa[np]=nq;for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;}}return; } void dfs(ll x){for(ll i=25;i>=0;i--){ll y=t[x][i];if(!y)continue;dep[y]=dep[x]+1;f[y][0]=x;dfs(y);}rk[x]=tot;tot+=len[x]-len[fa[x]];return; } signed main() {scanf("%s",s+1);n=strlen(s+1);for(ll i=1;i<=n;i++)scanf("%lld",&w[i]),w[i]+=w[i-1];last=cnt=1;for(ll i=n;i>=1;i--)Insert(s[i]-'a'),pos[last]=i,id[i]=last;for(ll i=2;i<=cnt;i++)t[fa[i]][s[pos[i]+len[fa[i]]]-'a']=i;dfs(1);for(ll j=1;j<=T;j++)for(ll i=1;i<=cnt;i++)f[i][j]=f[f[i][j-1]][j-1];for(ll p=1;p<=n;p++){ll x=id[p];for(ll i=T;i>=0;i--){ll y=f[x][i];if(y<=1)continue;if(rk[y]+1<=w[p+len[y]-1]-w[p-1])x=y;}ll l=len[fa[x]]+1,r=len[x];while(l<=r){ll mid=(l+r)>>1;if(rk[x]+len[x]-mid+1<=w[p+mid-1]-w[p-1])r=mid-1;else l=mid+1;}if(rk[x]+len[x]-l+1==w[p+l-1]-w[p-1])num++,p1[num]=p,p2[num]=p+l-1;}printf("%lld\n",num);for(ll i=1;i<=num;i++)printf("%lld %lld\n",p1[i],p2[i]);return 0; }

總結(jié)

以上是生活随笔為你收集整理的NOI.AC#2144-子串【SAM,倍增】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。