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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)

發(fā)布時(shí)間:2025/3/15 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

NOI2019考前做NOI2018題。。

題目鏈接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=5417
(luogu) https://www.luogu.org/problemnew/show/P4770
(uoj) http://uoj.ac/problem/395

題解: 其實(shí)非常水,轉(zhuǎn)化成\(S[l,r]\)\(T\)有多少本質(zhì)不同的公共子串,我們就是要求出串\(T\)每個(gè)位置與\(S[l,r]\)最長(zhǎng)匹配的長(zhǎng)度。那么就對(duì)\(S\)建SAM,把\(T\)\(S\)上跑,每次到達(dá)一個(gè)點(diǎn)之后如果它的子樹內(nèi)沒有\([l,r]\)之間的點(diǎn)就跳fa. 求子樹內(nèi)的點(diǎn)(即Right集合)可以自上而下線段樹合并。因?yàn)橐蟊举|(zhì)不同的公共子串個(gè)數(shù),那么對(duì)\(T\)也建立后綴自動(dòng)機(jī),統(tǒng)計(jì)T的后綴自動(dòng)機(jī)上每個(gè)點(diǎn)與S的公共子串個(gè)數(shù)。細(xì)節(jié)挺多,掛了好幾次,詳見代碼。

時(shí)間復(fù)雜度\(O(n\log n)\), \(n\)為輸入字符串總長(zhǎng)

字符串細(xì)節(jié)真的好多啊,提醒自己明天如果寫字符串題一定要對(duì)拍!(快算了吧,我必不可能會(huì)做字符串)

代碼

#include<cstdio> #include<cstdlib> #include<cstring> #include<cassert> #include<iostream> #include<algorithm> #define llong long long using namespace std;inline int read() {int x=0; bool f=1; char c=getchar();for(;!isdigit(c);c=getchar()) if(c=='-') f=0;for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');if(f) return x;return -x; }const int N = 5e5; const int S = 26; const int LGN = 21; struct SuffixAutomaton {int fa[(N<<1)+3];int len[(N<<1)+3];int son[(N<<1)+3][S+1];int ord[(N<<1)+3];int buc[(N<<1)+3];int rtn,siz,lstpos;void init(){for(int i=1; i<=siz; i++){len[i] = fa[i] = 0;for(int j=1; j<=S; j++) son[i][j] = 0;}rtn = siz = lstpos = 1;}int insertchar(int ch){int p = lstpos,np; siz++; np = lstpos = siz; len[np] = len[p]+1;for(; p && son[p][ch]==0; p=fa[p]) {son[p][ch] = np;}if(p==0) {fa[np] = 1;}else{int q = son[p][ch];if(len[q]==len[p]+1) {fa[np] = q;}else{siz++; int nq = siz; len[nq] = len[p]+1;memcpy(son[nq],son[q],sizeof(son[q]));fa[nq] = fa[q]; fa[np] = fa[q] = nq;for(; p && son[p][ch]==q; p=fa[p]) {son[p][ch] = nq;}}}return np;}void getord(){for(int i=1; i<=siz; i++) buc[i] = 0;for(int i=1; i<=siz; i++) buc[len[i]]++;for(int i=1; i<=siz; i++) buc[i] += buc[i-1];for(int i=siz; i>=1; i--) ord[buc[len[i]]--] = i;} } sam,samt; struct SegmentTree {int ls,rs; } sgt[(N<<1)*LGN+3]; char a[N+3],b[N+3]; int mxl[(N<<1)+3]; int rt[(N<<1)+3]; int n,m,q,rtn,siz;void addval(int &pos,int le,int ri,int lrb) {siz++; sgt[siz] = sgt[pos]; pos = siz;if(le==lrb && ri==lrb) {return;}int mid = (le+ri)>>1;if(lrb<=mid) {addval(sgt[pos].ls,le,mid,lrb);}else {addval(sgt[pos].rs,mid+1,ri,lrb);} }int merge(int pos1,int pos2) {if(pos1==0) return pos2; if(pos2==0) return pos1;siz++; int pos = siz;sgt[pos].ls = merge(sgt[pos1].ls,sgt[pos2].ls);sgt[pos].rs = merge(sgt[pos1].rs,sgt[pos2].rs);return pos; }int query(int pos,int le,int ri,int rb) //largest <=rb {if(pos==0) return 0;if(le==ri) return le;int mid = (le+ri)>>1;if(rb<=mid) return query(sgt[pos].ls,le,mid,rb);else{int tmp = query(sgt[pos].rs,mid+1,ri,rb);return tmp ? tmp : query(sgt[pos].ls,le,mid,rb);} }int main() {scanf("%s",a+1); n = strlen(a+1);for(int i=1; i<=n; i++) a[i] -= 96;sam.init();rtn = siz = 1;for(int i=1; i<=n; i++){int u = sam.insertchar(a[i]);rt[u] = rtn; addval(rt[u],1,n,i);}sam.getord();for(int i=sam.siz; i>=1; i--){int u = sam.ord[i];rt[sam.fa[u]] = merge(rt[sam.fa[u]],rt[u]);}scanf("%d",&q);while(q--){for(int i=1; i<=samt.siz; i++) mxl[i] = 0;samt.init();scanf("%s",b+1); m = strlen(b+1); int lb,rb; scanf("%d%d",&lb,&rb);for(int i=1; i<=m; i++) b[i]-=96;for(int i=1; i<=m; i++) samt.insertchar(b[i]);samt.getord();int u = sam.rtn,uu = samt.rtn,cur = 0; llong ans = 0ll;for(int i=1; i<=m; i++){while(u && sam.son[u][b[i]]==0) {u = sam.fa[u]; cur = sam.len[u];}if(u!=0) {u = sam.son[u][b[i]]; cur++;} else {u = sam.rtn; cur = 0;}int tmp = query(rt[u],1,n,rb);while(u && tmp<lb+sam.len[sam.fa[u]]) {u = sam.fa[u]; tmp = query(rt[u],1,n,rb); cur = min(sam.len[u],tmp-lb+1);}cur = min(cur,tmp-lb+1); //注意此處必須受到區(qū)間限制 while(uu && samt.son[uu][b[i]]==0) {uu = samt.fa[uu];}uu = uu==0 ? 1 : samt.son[uu][b[i]];mxl[uu] = max(mxl[uu],cur);}for(int i=samt.siz; i>=1; i--){int uu = samt.ord[i];mxl[samt.fa[uu]] = max(mxl[samt.fa[uu]],mxl[uu]);}for(int i=1; i<=samt.siz; i++){mxl[i] = max(min(mxl[i],samt.len[i]),samt.len[samt.fa[i]]); //最長(zhǎng)匹配的長(zhǎng)度,不得小于該點(diǎn)最短長(zhǎng)度 int tmp = samt.len[i]-mxl[i]; //該節(jié)點(diǎn)上不匹配的個(gè)數(shù)ans += (llong)tmp;}printf("%lld\n",ans);}return 0; }

總結(jié)

以上是生活随笔為你收集整理的UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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