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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

[十二省联考2019]字符串问题 后缀自动机 + 拓扑排序 + 最长路 + 倍增

發(fā)布時(shí)間:2023/12/20 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [十二省联考2019]字符串问题 后缀自动机 + 拓扑排序 + 最长路 + 倍增 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

題目描述:
給定一個(gè)長(zhǎng)串 $S$,給定若干 $S$ 的子串 $a_{i}$, $b_{i}$,再給出一些 $a$ 串和 $b$ 串的支配關(guān)系.
構(gòu)造一個(gè)長(zhǎng)度最長(zhǎng)的字符串,使得:
字符串只由 $a_{i}$ 組成.
當(dāng)且僅當(dāng) $a_{i}$ 所支配的一個(gè)串 $b_{i}$ 為 $a_{j}$ 的前綴,才可將 $a_{j}$ 連到 $a_{i}$ 后面.


首先,對(duì)于求前綴,我們可以對(duì) $S$ 的反串建立后綴自動(dòng)機(jī),這樣每個(gè) $f_{i}$ 將會(huì)轉(zhuǎn)移到當(dāng)前串的一個(gè)前綴,而不是后綴.

第一步,先將所有的 $a$串和$b$串都定位到后綴自動(dòng)機(jī)上.
對(duì)于這一步,我們使用倍增算法.

第二部,我們將支配邊進(jìn)行連邊(即再后綴樹中對(duì)應(yīng)的點(diǎn)和點(diǎn)).

順著后綴樹中的邊和支配邊進(jìn)行轉(zhuǎn)移即可.
細(xì)節(jié)部分看一下代碼.

?

Code:

#include <cstdio> #include <algorithm> #include <cstring> #include <queue> #include <vector> #define setIO(s) freopen(s".in","r",stdin)// ,freopen(s".out","w",stdout) #define maxn 1500001 using namespace std; char str[maxn]; int debug; int length,na,nb,m; namespace SAM{#define N 29 #define ll long long #define L l=length-l+1#define R r=length-r+1 int last,tot; vector<int>ed[maxn]; int la[maxn],ra[maxn],h[maxn]; int ch[maxn][N],dis[maxn]; int f[maxn],trace[maxn],mk[maxn],pos[maxn]; int F[24][maxn]; int head[maxn],to[maxn],nex[maxn],edges; int ge[maxn]; int vis[maxn]; int trc; queue<int>Q; int du[maxn]; long long DP[maxn]; int idx[maxn]; int cmp(int i,int j){ if(h[i]==h[j]) return i>j; return h[i]<h[j]; }void add(int u,int v){nex[++edges] = head[u],head[u]=edges,to[edges]=v; mk[edges]=0; //if(!debug) printf("%d %d\n",u,v); }//SAM of S void ins(int c,int i){ int np = ++tot, p = last; last = np; dis[np] = i; while(p && !ch[p][c]) ch[p][c] = np, p = f[p]; if(!p) f[np] = 1;else { int q = ch[p][c]; if(dis[q] == dis[p] + 1) f[np] = q;else {int nq = ++tot; dis[nq] = dis[p] + 1; memcpy(ch[nq],ch[q],sizeof(ch[q])); f[nq] = f[q], f[np] = f[q] = nq; while(p && ch[p][c] == q) ch[p][c] = nq,p = f[p]; }}trace[i] = np; } void DFS(int u){ F[0][u]=f[u];for(int i=1;i<24;++i) F[i][u]=F[i-1][F[i-1][u]]; int sz=ed[u].size(); for(int i=0;i<sz;++i) DFS(ed[u][i]); ed[u].clear(); } void build(){int l,r;scanf("%d",&na);for(int i=1;i<=na;++i){scanf("%d%d",&l,&r); L;R; swap(l,r); la[i]=l,ra[i]=r,h[i]=r-l+1; } scanf("%d",&nb); for(int i=1;i<=nb;++i) {scanf("%d%d",&l,&r); L;R; swap(l,r); la[i+na]=l,ra[i+na]=r,h[i+na]=r-l+1; }for(int i=2;i<=tot;++i) ed[f[i]].push_back(i); trc=tot; DFS(1); for(int i=1;i<=na+nb;++i) {int p=trace[ra[i]]; for(int j=23;j>=0;--j) if(dis[F[j][p]] >= h[i]) p=F[j][p]; if(dis[p]==h[i]) pos[i]=p; else ed[p].push_back(i); }}void sol(){int nn=tot; for(int i=2;i<=nn;++i){ if(ed[i].size()==0){ add(f[i],i); continue; }int p=0; int sz=ed[i].size(); for(int j=0;j<sz;++j) ge[++p]=ed[i][j]; // sort(ge+1,ge+1+p,cmp); //將該點(diǎn)對(duì)應(yīng)字符串按長(zhǎng)度排序 int lst=f[i]; for(int j=1;j<=p;++j) { add(lst,++tot); dis[tot] = h[ge[j]]; pos[ge[j]]=tot; lst=tot; }add(pos[ge[p]],i); ed[i].clear(); }}void get(){int a,b; scanf("%d%d",&a,&b); add(pos[a],pos[b+na]); mk[edges]=1; } void solve(){Q.push(1); du[1]=0; long long ans=0; for(int i=1;i<=edges;++i) ++du[to[i]]; while(!Q.empty()){int u=Q.front(); Q.pop(); for(int i=head[u];i;i=nex[i]){--du[to[i]]; long long ed=DP[u]+dis[to[i]]-(mk[i]?0:dis[u]); DP[to[i]]=max(DP[to[i]],ed); if(du[to[i]]==0) Q.push(to[i]); }} for(int i=1;i<=tot;++i) if(du[i]!=0) {printf("-1\n"); return; }for(int i=1;i<=na;++i) ans=max(ans,DP[pos[i]]); printf("%lld\n",ans); }void init(){for(int i=1;i<=tot;++i) DP[i]=0; for(int i=1;i<=tot;++i) dis[i]=0; for(int i=1;i<=edges;++i) mk[i]=0; for(int i=1;i<=tot;++i) head[i]=0; for(int i=0;i<=tot;++i) du[i]=0; for(int i=1;i<=trc;++i) for(int j=0;j<=22;++j) F[j][i]=0; for(int i=1;i<=trc;++i) memset(ch[i],0,sizeof(ch[i])); last=tot=1,edges=0; } }; int main(){//setIO("string9"); int T; scanf("%d",&T); while(T--){ debug=T; SAM::last=SAM::tot=1; scanf("%s",str+1),length=strlen(str+1); for(int i=length;i;--i) SAM::ins(str[i]-'a',length-i+1); SAM::trc=SAM::tot; SAM::build(); SAM::sol(); scanf("%d",&m); for(int i=1;i<=m;++i) SAM::get(); SAM::solve(); SAM::init(); }return 0; }

  

轉(zhuǎn)載于:https://www.cnblogs.com/guangheli/p/10679544.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的[十二省联考2019]字符串问题 后缀自动机 + 拓扑排序 + 最长路 + 倍增的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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