【十二省联考2019】字符串问题【后缀自动机】【拓扑排序】
生活随笔
收集整理的這篇文章主要介紹了
【十二省联考2019】字符串问题【后缀自动机】【拓扑排序】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題意:給一個字符串 SSS,以子串的形式給出一些 A 類串和 B 類串以及 mmm 對 A 類串支配 B 類串的關系。求一個總長度最長的 A 類串序列,使得每個串都存在一個 B 類串前綴被后一個串支配。無窮輸出 ?1-1?1。
∣S∣,m≤2×105|S|,m\leq 2\times 10^5∣S∣,m≤2×105
顯然需要先把反串 SAM 建出來,然后樹上倍增定位每個子串。
對于每一步,相當于是可以不斷丟掉最后一個字符變成前綴,如果是個 B 類串,可以走到一個被支配的 A 類串。
所以 SAM 上每個點向 fail 樹上的父親連一條 000 的邊, uuu 支配 vvv 就從 vvv 往 uuu 連 ∣u∣|u|∣u∣ 的邊。然后拓撲排序后做最長路即可。因為沒有 000 環,也沒有不聯通等奇奇怪怪的東西,所以有環就是 ?1-1?1。
然后這道題就做完了……
才怪。你的 A,B 類串定位的是一類子串,而限制是死的。具體來說,一個 B 類串定位到了某個結點的一坨子串上,然后一個 A 類串定位也定位到了這個結點,而且還比 B 類串短,那么它是不能走到這個 B 的。但是 SAM 上它們是同一個結點,所以上面的算法就會出錯。
怎么辦?拆了唄
當然不用真的拆了,把這些定位的點放結點的 vector 里,然后在內部排序建一下就可以了。
不算難寫,單純的碼量大。
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <vector> #include <utility> #include <queue> #include <algorithm> #define MAXN 800005 #define MAXM 1200005 using namespace std; typedef long long ll; inline int read() {int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans; } char s[MAXN]; int ch[MAXN][26],len[MAXN],fa[MAXN],las=1,tot=1; void insert(int c) {int cur=++tot;len[cur]=len[las]+1;int p=las;for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;if (!p) fa[cur]=1;else{int q=ch[p][c];if (len[q]==len[p]+1) fa[cur]=q;else{int _q=++tot;len[_q]=len[p]+1;fa[_q]=fa[q],fa[cur]=fa[q]=_q;memcpy(ch[_q],ch[q],sizeof(ch[_q]));for (;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=_q;}}las=cur; } int up[MAXN][20]; inline void build() {for (int i=1;i<=tot;i++) up[i][0]=fa[i];for (int j=1;j<20;j++)for (int i=1;i<=tot;i++)up[i][j]=up[up[i][j-1]][j-1]; } inline int find(int x,int l) {for (int i=19;i>=0;i--)if (len[up[x][i]]>=l)x=up[x][i];return x; } int na,nb,la[MAXN],lb[MAXN],ra[MAXN],rb[MAXN]; struct node { int idx,type;inline int len()const{return type? ra[idx]-la[idx]+1:rb[idx]-lb[idx]+1;}inline int pos()const{if (type==1) return tot+idx;if (type==0) return tot+na+idx;return idx;} }; inline bool operator <(const node& a,const node& b){return a.len()<b.len()||(a.len()==b.len()&&a.type<b.type);} vector<node> lis[MAXN]; struct edge{int u,v,w;}e[MAXM]; int head[MAXN],nxt[MAXM],deg[MAXN],cnt; inline void addnode(int u,int v,int w) {e[++cnt]=(edge){u,v,w};nxt[cnt]=head[u];head[u]=cnt;++deg[v]; } int pos[MAXN],dfn[MAXN],tim; ll dp[MAXN]; int q[MAXN],qs,qt; void clear() {for (int i=1;i<=tot;i++) {memset(ch[i],0,sizeof(ch[i])),fa[i]=len[i]=0;memset(up[i],0,sizeof(up[i]));vector<node>().swap(lis[i]);}for (int i=1;i<=tot+na+nb+1;i++) head[i]=deg[i]=dp[i]=0;for (int i=1;i<=cnt;i++) nxt[i]=0;tim=cnt=0,las=tot=1; } int main() {for (int T=read();T;T--){scanf("%s",s+1);int n=strlen(s+1);for (int i=n;i>=1;i--) insert(s[i]-'a'),pos[i]=las;build(); na=read();for (int i=1;i<=na;i++){la[i]=read(),ra[i]=read();lis[find(pos[la[i]],ra[i]-la[i]+1)].push_back((node){i,1});}nb=read();for (int i=1;i<=nb;i++){lb[i]=read(),rb[i]=read();lis[find(pos[lb[i]],rb[i]-lb[i]+1)].push_back((node){i,0});}for (int i=1;i<=tot;i++) {sort(lis[i].begin(),lis[i].end());lis[i].push_back((node){i,-1});if (fa[i]) addnode(lis[i].front().pos(),fa[i],0);for (int j=0;j<(int)lis[i].size()-1;j++) addnode(lis[i][j+1].pos(),lis[i][j].pos(),0);}for (int m=read();m;m--){int u,v;u=read(),v=read();addnode(tot+na+v,tot+u,ra[u]-la[u]+1);}int S=tot+na+nb+1;for (int i=1;i<=na;i++) addnode(S,tot+i,ra[i]-la[i]+1);qs=1,qt=0;for (int i=1;i<=S;i++) if (!deg[i]) q[++qt]=i;while (qs<=qt){int u=dfn[++tim]=q[qs++];for (int i=head[u];i;i=nxt[i])if (!(--deg[e[i].v]))q[++qt]=e[i].v;}if (tim<S) puts("-1");else{for (int k=S;k>=1;k--){int u=dfn[k];for (int i=head[u];i;i=nxt[i])dp[u]=max(dp[u],dp[e[i].v]+e[i].w);}printf("%lld\n",dp[S]);}clear();}return 0; }總結
以上是生活随笔為你收集整理的【十二省联考2019】字符串问题【后缀自动机】【拓扑排序】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 儿童减肥的最好方法是什么
- 下一篇: 9岁男孩怎么减肥效果好