HDU - 5008 Boring String Problem(后缀树求本质不同第k大子串)
題目鏈接:點(diǎn)擊查看
題目大意:給出一個(gè)長(zhǎng)度為 nnn 的字符串,再給出 mmm 次詢問(wèn),每次詢問(wèn)需要輸出本質(zhì)不同第 kkk 小的子串的起止位置。如果有多個(gè)答案,輸出起點(diǎn)最小的那個(gè)。強(qiáng)制在線。
題目分析:后綴樹(shù)的模板題,在后綴樹(shù)上按照字典序前序遍歷維護(hù)一下前綴和(子串個(gè)數(shù)),然后對(duì)于每次詢問(wèn)就可以二分查找答案在哪個(gè)節(jié)點(diǎn)了。
具體難點(diǎn)有兩條:
對(duì)應(yīng)的解決方案如下:
那么如何求后綴樹(shù)呢?其實(shí)只需要將字符串反轉(zhuǎn)一下,然后扔到后綴自動(dòng)機(jī)里,此時(shí)求出的 parentparentparent 樹(shù)就是后綴樹(shù)了。
原理也十分簡(jiǎn)單,考慮 parentparentparent 樹(shù)的葉子節(jié)點(diǎn)到根節(jié)點(diǎn)的一條路徑,表示的是一個(gè)前綴的所有后綴,反轉(zhuǎn)一下自然就變成了一個(gè)后綴的所有前綴,也就是后綴樹(shù)了。
因?yàn)楸绢}需要記錄相應(yīng)子串的首次出現(xiàn)位置,所以可以維護(hù)一下反串中每個(gè)節(jié)點(diǎn)的 endposendposendpos 的最大值用于答案輸出。
剩下的就是億點(diǎn)點(diǎn)坐標(biāo)轉(zhuǎn)換的細(xì)節(jié)了,大家可以用樣例以及:
abcd
1
0
ans=1 1
這組樣例自己調(diào)一下,都能調(diào)過(guò)去的話應(yīng)該就沒(méi)有問(wèn)題了。
代碼:
// #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> #include<list> #include<unordered_map> #define lowbit(x) (x&-x) using namespace std; typedef long long LL; typedef unsigned long long ull; template<typename T> inline void read(T &x) {T f=1;x=0;char ch=getchar();while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();x*=f; } template<typename T> inline void write(T x) {if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0'); } const int inf=0x3f3f3f3f; const int N=1e5+100; char s[N]; vector<int>node[N<<1]; int tot,last,endpos[N<<1]; LL sum[N<<1]; int rk[N<<1],dfn; struct Node {int ch[26];int fa,len; }st[N<<1]; inline int newnode() {tot++;for(int i=0;i<26;i++)st[tot].ch[i]=0;st[tot].fa=st[tot].len=0;endpos[tot]=0;node[tot].clear();return tot; } void add(int x) {int p=last,np=last=newnode();st[np].len=st[p].len+1;while(p&&!st[p].ch[x])st[p].ch[x]=np,p=st[p].fa;if(!p)st[np].fa=1;else{int q=st[p].ch[x];if(st[p].len+1==st[q].len)st[np].fa=q;else{int nq=newnode();st[nq]=st[q]; st[nq].len=st[p].len+1;st[q].fa=st[np].fa=nq;while(p&&st[p].ch[x]==q)st[p].ch[x]=nq,p=st[p].fa;//向上把所有q都替換成nq}} } void dfs_pos(int u) {for(auto v:node[u]) {dfs_pos(v);endpos[u]=max(endpos[u],endpos[v]);} } void dfs(int u,int fa) {if(u!=1) {dfn++;sum[dfn]=sum[dfn-1]+(st[u].len-st[fa].len);rk[dfn]=u;}vector<pair<char,int>>son;for(auto v:node[u]) {son.push_back({s[endpos[v]-st[u].len],v});}sort(son.begin(),son.end());for(auto it:son) {dfs(it.second,u);} } void build() {for(int i=1;i<=tot;i++) {node[st[i].fa].push_back(i);}dfs_pos(1);dfs(1,-1); } void init() {last=1;dfn=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);while(scanf("%s",s+1)!=EOF) {init();int n=strlen(s+1);reverse(s+1,s+1+n);for(int i=1;i<=n;i++) {add(s[i]-'a');endpos[last]=i;}build();int m;scanf("%d",&m);LL l=0,r=0;while(m--) {LL k;scanf("%lld",&k);k=(k^l^r)+1;if(k<=sum[dfn]) {int t=lower_bound(sum+1,sum+1+dfn,k)-sum;int u=rk[t];int len=st[u].len-(sum[t]-k);l=n-endpos[u]+1;r=l+len-1;} else {l=r=0;}printf("%lld %lld\n",l,r);}}return 0; }總結(jié)
以上是生活随笔為你收集整理的HDU - 5008 Boring String Problem(后缀树求本质不同第k大子串)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: CodeForces - 1579G M
- 下一篇: 2021CCPC(桂林) - Suffi