生活随笔
收集整理的這篇文章主要介紹了
洛谷 - P3975 [TJOI2015]弦论(后缀自动机)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
題目鏈接:點擊查看
題目大意:給出一個字符串 s,再給出一次詢問,詢問分為兩種類型:
0 k:如果不同位置的相同子串算作一個,求第 k 小的子串1 k:如果不同位置的相同子串算作多個,求第 k 小的子串
題目分析:溫故而知新,稍微總結(jié)一下這次重學(xué) SAM 的一點小收貨吧:
SAM 中的連邊只有兩種,一種是 trie 圖上的,一種是 parent 樹上的,前者是 DAG,后者是樹一般看到 SAM 會配合基數(shù)排序然后倒著維護答案,這個過程實際上模擬的是在 parent 樹上的 dfs,更直觀的理解就是,將 parent 樹建出來,然后直接在樹上維護信息即可如果想要維護 dp 的話,在 trie 圖上需要在 DAG 上跑拓撲,在 parent 樹上跑樹形 dp順著 trie 圖跑的話可以得到子串的前綴,順著 parent 樹往上跳 father 的話可以得到后綴的后綴(應(yīng)該可以這樣理解)
回到這個題目中來,第一個問題就是如何在 SAM 上表示子串,因為從源點開始順著 trie 圖一直走,每到一個新的節(jié)點就代表著一個新的子串,所以在 trie 圖上拓撲排序一下求出可達的路徑數(shù),實質(zhì)上就是求出了從某個點開始,繼續(xù)走下去可以到達的子串個數(shù)
第二個問題就是,如何區(qū)分 “位置不同的相同子串算一個” 和 “位置不同的相同子串算多個”,首先對于 trie 圖而言,其每個不同的節(jié)點都代表著一個本質(zhì)不同的子串,所以初始時將每個節(jié)點的 cnt[ i ] 置為 1,然后在 DAG 上拓撲計算一下路徑的個數(shù)就好了
如果位置不同的串算多個,也就是說需要額外維護一下每個節(jié)點 endpos 的個數(shù),因為 endpos 的定義就是 “以當前節(jié)點為后綴在原串中出現(xiàn)的位置 的 集合”,維護 endpos 位置的個數(shù)這個在構(gòu)建自動機時,令所有非復(fù)制而來的節(jié)點賦值為 1 ,最后在 parent 樹上 dp 一下就好了,然后再同上在 trie 圖上拓撲一下就能求出路徑個數(shù)
至于求解的話,dfs 貪心去找合適的路徑就可以了
代碼:
?
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e6+100;char s[N];LL cnt[N<<1],sum[N<<1];vector<int>node[N<<1];bool vis[N<<1];struct Node
{int ch[26];int fa,len;
}t[N<<1];int tot,last;int newnode()
{tot++;for(int i=0;i<26;i++)t[tot].ch[i]=0;t[tot].fa=t[tot].len=0;return tot;
}void add(int x)
{int p=last,np=last=newnode();t[np].len=t[p].len+1;cnt[np]=1;while(p&&!t[p].ch[x])t[p].ch[x]=np,p=t[p].fa;if(!p)t[np].fa=1;else{int q=t[p].ch[x];if(t[p].len+1==t[q].len)t[np].fa=q;else{int nq=newnode();t[nq]=t[q];t[nq].len=t[p].len+1;t[q].fa=t[np].fa=nq;while(p&&t[p].ch[x]==q)t[p].ch[x]=nq,p=t[p].fa;}}
}void dfs1(int u)//parent樹上dp
{for(auto v:node[u]){dfs1(v);cnt[u]+=cnt[v];}
}void dfs2(int u)//DAG上dp
{if(vis[u])return;vis[u]=true;for(int i=0;i<26;i++){if(!t[u].ch[i])continue;dfs2(t[u].ch[i]);sum[u]+=sum[t[u].ch[i]];}
}void print(int u,int k)
{if(k<=cnt[u])return;k-=cnt[u];for(int i=0;i<26;i++){int v=t[u].ch[i];if(!v)continue;if(sum[v]<k){k-=sum[v];continue;}putchar('a'+i);print(v,k);return;}
}void init()
{last=1;tot=0;newnode();
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int op,k;init();scanf("%s%d%d",s,&op,&k);int n=strlen(s);for(int i=0;i<n;i++)add(s[i]-'a');for(int i=1;i<=tot;i++)node[t[i].fa].push_back(i);dfs1(1);for(int i=1;i<=tot;i++)sum[i]=op?cnt[i]:(cnt[i]=1);sum[1]=cnt[1]=0;dfs2(1);if(k>sum[1])puts("-1");elseprint(1,k);return 0;
}
?
總結(jié)
以上是生活随笔為你收集整理的洛谷 - P3975 [TJOI2015]弦论(后缀自动机)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。