题目大意:
对于一个给定字符串,找到其所有不同的子串中排第k小的子串
先构建后缀自动机,然后我们可以将整个后缀自动机看做是一个DAG图,那么我们先进行拓扑排序得到 *b[N]
对于每个节点记录一个sc值,表示当前节点往下走可以得到不同的字符串的个数
然后从后往前,每次到达一个节点,当前节点sc赋1,然后每个可以往下走的son节点,都把这个son上的sc加到当前节点上即可
接下来得到一个排名,从root开始走,从a~z循环,通过sc正确的找到下一个进入的节点
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 5 using namespace std; 6 #define N 90005 7 8 struct SamNode{ 9 int l , sc; 10 SamNode *son[26] , *f; 11 }sam[N<<1] , *root , *last , *b[N<<1]; 12 13 int cnt , num[N] , n , k; 14 char s[N]; 15 void add(int x) 16 { 17 SamNode *p = &sam[++cnt] , *jp = last; 18 p->l = jp->l+1; 19 last = p; 20 for(; jp&&!jp->son[x] ; jp=jp->f) jp->son[x] = p; 21 if(!jp) p->f = root; 22 else{ 23 if(jp->l+1 == jp->son[x]->l) p->f = jp->son[x]; 24 else{ 25 SamNode *r = &sam[++cnt] , *q = jp->son[x]; 26 *r = *q; r->l = jp->l+1; 27 p->f = q->f = r; 28 for( ; jp&&jp->son[x]==q ; jp=jp->f) jp->son[x] = r; 29 } 30 } 31 } 32 33 void build() 34 { 35 int len = strlen(s); 36 for(int i=0 ; i<len ; i++) add(s[i]-'a'); 37 for(int i=0 ; i<=cnt ; i++) num[sam[i].l]++; 38 for(int i=1 ; i<=len ; i++) num[i]+=num[i-1]; 39 for(int i=0 ; i<=cnt ; i++) b[--num[sam[i].l]] = &sam[i]; 40 41 for(int i=cnt ; i>=1 ; i--){ 42 b[i]->sc=1; 43 for(int j=0 ; j<26 ; j++){ 44 if(b[i]->son[j]) 45 b[i]->sc+=b[i]->son[j]->sc; 46 } 47 } 48 } 49 50 void solve() 51 { 52 scanf("%d" , &n); 53 char tmp[N]; 54 int val , t;//t表示tmp中的位数 55 while(n--){ 56 scanf("%d" , &k); 57 SamNode *cur = root; 58 val = 0 , t=0; 59 while(val<k){ 60 for(int i=0 ; i<26 ; i++){ 61 if(cur->son[i]){ 62 if(val+cur->son[i]->sc<k) val+=cur->son[i]->sc; 63 else{ 64 val++; 65 tmp[t++] = i+'a'; 66 cur = cur->son[i]; 67 break; 68 } 69 } 70 } 71 } 72 tmp[t]='