SPOJ 7258 字典序第K小的子串:后缀自动机

来源:互联网 发布:网络词cpg是什么意思 编辑:程序博客网 时间:2024/06/03 20:34

题意:给出一个串,求出所有子串,去重,然后求字典序第K小的串,总共Q组询问。


题解:对串创建SAM,然后,拓扑一下,算出每个状态,如果继续往后边走,能够生成多少种不同的字符串。然后每次查询的时候,就按照字典顺序来看,比如到达某个点S,先看'a'儿子能造出多少个字符,如果很少,那么我们下一个字符肯定不走‘a’,那么我们继续看'b',看一下'a'和‘b’共能造出多少个字符,如果多了,那就选‘b’,如果还不够,那就继续看‘a’、‘b’、‘c’,在这个DAG上边爬一爬就行了。具体细节看一下代码。


Code:

#include<bits/stdc++.h>using namespace std;const int maxn = 9e4+1000;int len;char s[maxn];int cntA[maxn];vector<char> ans;struct SAM{int last,cnt,nxt[maxn*2][26],fa[maxn*2],l[maxn*2];int rk[maxn*2],num[maxn*2];void init(){last = cnt=1;memset(nxt[1],0,sizeof nxt[1]);fa[1]=l[1] =0;}void add(int c){int p = last;int np = ++cnt;last = np;l[np] =l[p]+1;while (p&&!nxt[p][c]){nxt[p][c] = np;p = fa[p];}if (!p){fa[np] =1;}else{int q = nxt[p][c];if (l[q]==l[p]+1){fa[np] =q;}else{int nq = ++cnt;memcpy(nxt[nq],nxt[q],sizeof nxt[q]);fa[nq] =fa[q];l[nq] = l[p]+1;fa[np] =fa[q] =nq;while (nxt[p][c]==q){nxt[p][c]=nq;p = fa[p];}}}}void build (){for (int i=1;i<=cnt;i++){cntA[l[i]]++;}for (int i=1;i<maxn-5;i++){cntA[i]+=cntA[i-1];}for (int i=1;i<=cnt;i++){rk[cntA[l[i]]--] =i;}for (int i=1;i<=cnt;i++){num[i]=1;}for (int i=cnt;i>=1;i--){int x = rk[i];for (int i=0;i<26;i++){if (nxt[x][i]){num[x]+=num[nxt[x][i]];}}}num[0]=0;}inline void print(){for (char t:ans){printf("%c",t);}printf("\n");}void query(int K){ans.clear();int now=1;int sum=0;while (true){//cout<<now<<' '<<sum<<endl; if (sum==K){print();return ;}int c=0;int last =0;while (sum<K){sum+=num[nxt[now][c]];c++;assert(c<27);}c--;sum-=num[nxt[now][c]];now = nxt[now][c];ans.push_back('a'+c);sum++;}}}sam;int main(){int Q;scanf("%s%d",s,&Q);sam.init();int le = strlen(s);for (int i=0;i<le;i++){sam.add(s[i]-'a');}sam.build();while (Q--){int x;scanf("%d",&x);sam.query(x);}return 0;}


阅读全文
0 0