题目大意:
给定一个字符串,可理解成环,然后选定一位置,逆时针或顺时针走一遍,希望得到字典序最大,如果同样大,希望找到起始位置最小的,如果还相同,就默认顺时针
比赛一直因为处理最小位置出错,一结束就想明白了。。。真是作孽
这里正向后缀自动机跑一遍最大的,这样得到的位置肯定是最小的
而逆时针最大就反向重建后缀自动机再跑一遍最大的,但这样因为后缀自动机跑出的位置是最小的,但返回来就变成最大的位置了
如果存在更小的位置,那么就相当于可以从开头拎出一段移到末尾,那么必然是产生一个循环节,这个长度可以利用kmp来处理
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <vector> 5 #include <queue> 6 using namespace std; 7 /* run this program using the console pauser or add your own getch, system("pause") or input loop */ 8 #define M 26 9 #define N 80100 10 #define ull unsigned long long 11 char str[N] , tmp[N]; 12 int n , num[N]; 13 queue<int> Q; 14 15 struct SamNode{ 16 SamNode *son[M] , *f; 17 int l , s; 18 void init(){ 19 for(int i=0 ; i<M ; i++) son[i] = NULL; 20 f = NULL; 21 l = s = 0; 22 } 23 }; 24 25 SamNode sam[N] , *root , *last; 26 int cnt; 27 28 void init(){ 29 cnt = 0; 30 memset(sam , 0 , sizeof(sam)); 31 root = last = &sam[0]; 32 } 33 34 void add(int x){ 35 sam[cnt+1].init(); 36 SamNode *p = &sam[++cnt] , *jp=last; 37 /* 38 这里p->s = jp->s+1写成p->s = p->l也是一样的,因为对于每次当前的last来说, 39 l和s的值是一样的,因为每次当前的last都是处于字符串的位置上的 40 数,不是额外添加的节点 41 */ 42 p->l = jp->l+1; p->s = jp->s+1; 43 last = p; 44 for(; jp&&!jp->son[x] ; jp=jp->f) jp->son[x] = p; 45 if(!jp) p->f=root; 46 else{ 47 if(jp->l+1 == jp->son[x]->l) p->f = jp->son[x]; 48 else{ 49 sam[cnt+1].init(); 50 SamNode *r = &sam[++cnt] , *q = jp->son[x]; 51 *r=*q; 52 r->l = jp->l+1 ; r->s = p->l; 53 q->f = p->f = r; 54 for(; jp && jp->son[x]==q ; jp=jp->f) jp->son[x]=r; 55 } 56 } 57 } 58 ull _hash = 0; 59 60 int _next[N]; 61 int s[N]; 62 void get_next(int *s , int n) 63 { 64 _next[0] = _next[1] = 0; 65 for(int i=2; i <n ; i++){ 66 int j=_next[i-1]; 67 while(j && s[j+1]!=s[i]) j =_next[j]; 68 _next[i] = s[j+1]==s[i]?j+1:0; 69 } 70 } 71 72 int solve(int len) 73 { 74 SamNode *cur = root; 75 for(int i=0 ; i<len ; i++){ 76 for(int j=25 ; j>=0 ; j--){ 77 if(cur->son[j]){ 78 s[i] = j; 79 cur = cur->son[j]; 80 break; 81 } 82 } 83 } 84 int ret = cur->s-len+1; 85 86 return ret; 87 } 88 89 int main() { 90 // freopen("a.in" , "r" , stdin); 91 // freopen("out.txt" , "w" , stdout); 92 int T; 93 scanf("%d" , &T); 94 while(T--){ 95 scanf("%d" , &n); 96 scanf("%s" , str); 97 int len = n; 98 init(); 99 for(int i=0 ; i<len ; i++) 100 str[len+i] = str[i]; 101 for(int i=0 ; i<len*2 ; i++) 102 add(str[i]-'a'); 103 // for(int i=0 ; i<2*n ; i++) cout<<str[i]; 104 //cout<<endl; 105 int ret1 = solve(len); 106 107 for(int i=0 ; i<len ; i++){ 108 tmp[len-i-1] = str[i]; 109 } 110 for(int i=0 ; i<len ; i++){ 111 tmp[len+i] = tmp[i]; 112 } 113 114 init(); 115 for(int i=0 ; i<len*2 ; i++) 116 add(tmp[i]-'a'); 117 int ret2 = solve(len); 118 ret2 = len-ret2+1; 119 // cout<<"here: "<<ret1<<" "<<ret2<<endl; 120 get_next(s , n); 121 int del = n-_next[n-1]-1; 122 // cout<<"try: "<<del<<" "<<_next[n-1]<<endl; 123 if(n%del==0){ 124 while(ret2>del) ret2-=del; 125 } 126 // cout<<"here: "<<ret1<<" "<<ret2<<endl; 127 // cout<<ret1<<" "<<ret2<<endl; 128 int i , j , cnt , pos , wise=-1; 129 for(i=ret1-1 , j=ret2+len-1 , cnt=0 ; cnt<len ; cnt++ , i++ , j--){ 130 // cout<<cnt<<" "<<i<<" "<<j<<" "<<str[i]<<" "<<str[j]<<endl; 131 if(str[i]>str[j]){wise=0;pos=ret1;break;} 132 else if(str[i]<str[j]){wise=1;pos=ret2;break;} 133 } 134 if(wise==-1){ 135 if(ret1<ret2) pos = ret1 , wise = 0; 136 else if(ret1>ret2) pos = ret2 , wise = 1; 137 else pos=ret1 , wise=0; 138 // cout<<"in: "<<endl; 139 } 140 printf("%d %d\n" , pos, wise); 141 } 142 return 0; 143 }
后来终于受人指点知道了直接在后缀自动机上找到最后一个位置得到的最大字典序怎么求了,这样就不用求kmp了
后缀自动机上s记录达到的最长的位置,如果不更新,那么必然一次跑完得到的是最小位置,那么为了得到最大,之前更新每一个节点中的s,只有儿子位置上的能更新父亲
所以先将后缀自动机拓扑排序,然后从后往前,不断将父亲的s更新成更大的s
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <vector> 5 #include <queue> 6 using namespace std; 7 /* run this program using the console pauser or add your own getch, system("pause") or input loop */ 8 #define M 26 9 #define N 80100 10 #define ull unsigned long long 11 char str[N] , tmp[N]; 12 int n , num[N]; 13 queue<int> Q; 14 15 struct SamNode{ 16 SamNode *son[M] , *f; 17 int l , s; 18 void init(){ 19 for(int i=0 ; i<M ; i++) son[i] = NULL; 20 f = NULL; 21 l = s = 0; 22 } 23 }*b[N]; 24 25 SamNode sam[N] , *root , *last; 26 int cnt; 27 28 void init(){ 29 cnt = 0; 30 sam[0].init(); 31 root = last = &sam[0]; 32 } 33 34 void add(int x){ 35 sam[cnt+1].init(); 36 SamNode *p = &sam[++cnt] , *jp=last; 37 /* 38 这里p->s = jp->s+1写成p->s = p->l也是一样的,因为对于每次当前的last来说, 39 l和s的值是一样的,因为每次当前的last都是处于字符串的位置上的 40 数,不是额外添加的节点 41 */ 42 p->l = jp->l+1; p->s = jp->s+1; 43 last = p; 44 for(; jp&&!jp->son[x] ; jp=jp->f) jp->son[x] = p; 45 if(!jp) p->f=root; 46 else{ 47 if(jp->l+1 == jp->son[x]->l) p->f = jp->son[x]; 48 else{ 49 sam[cnt+1].init(); 50 SamNode *r = &sam[++cnt] , *q = jp->son[x]; 51 *r=*q; 52 r->l = jp->l+1 ; r->s = p->l; 53 q->f = p->f = r; 54 for(; jp && jp->son[x]==q ; jp=jp->f) jp->son[x]=r; 55 } 56 } 57 } 58 59 int solve(int len) 60 { 61 SamNode *cur = root; 62 for(int i=0 ; i<len ; i++){ 63 for(int j=25 ; j>=0 ; j--){ 64 if(cur->son[j]){ 65 cur = cur->son[j]; 66 break; 67 } 68 } 69 } 70 int ret = cur->s-len+1; 71 72 return ret; 73 } 74 75 int solve1(int len) 76 { 77 memset(num , 0 , sizeof(num)); 78 for(int i=1 ; i<=cnt ; i++) num[sam[i].l]++; 79 for(int i=1 ; i<=len*2 ; i++) num[i] += num[i-1]; 80 for(int i=1 ; i<=cnt ; i++) b[--num[sam[i].l]] = &sam[i]; 81 for(int i=cnt-1 ; i>=0 ; i--){ 82 b[i]->f->s = max(b[i]->f->s , b[i]->s); 83 } 84 SamNode *cur = root; 85 for(int i=0 ; i<len ; i++){ 86 for(int j=25 ; j>=0 ; j--){ 87 if(cur->son[j]){ 88 cur = cur->son[j]; 89 break; 90 } 91 } 92 } 93 int ret = cur->s-len+1; 94 return ret; 95 } 96 97 int main() { 98 // freopen("a.in" , "r" , stdin); 99 // freopen("out.txt" , "w" , stdout); 100 int T; 101 scanf("%d" , &T); 102 while(T--){ 103 scanf("%d" , &n); 104 scanf("%s" , str); 105 int len = n; 106 init(); 107 for(int i=0 ; i<len ; i++) 108 str[len+i] = str[i]; 109 for(int i=0 ; i<len*2 ; i++) 110 add(str[i]-'a'); 111 // for(int i=0 ; i<2*n ; i++) cout<<str[i]; 112 //cout<<endl; 113 int ret1 = solve(len); 114 115 for(int i=0 ; i<len ; i++){ 116 tmp[len-i-1] = str[i]; 117 } 118 for(int i=0 ; i<len ; i++){ 119 tmp[len+i] = tmp[i]; 120 } 121 122 init(); 123 for(int i=0 ; i<len*2-1 ; i++) 124 add(tmp[i]-'a'); 125 int ret2 = len-solve1(len)+1; 126 127 // cout<<"here: "<<ret1<<" "<<ret2<<endl; 128 // cout<<ret1<<" "<<ret2<<endl; 129 int i , j , cnt , pos , wise=-1; 130 for(i=ret1-1 , j=ret2+len-1 , cnt=0 ; cnt<len ; cnt++ , i++ , j--){ 131 // cout<<cnt<<" "<<i<<" "<<j<<" "<<str[i]<<" "<<str[j]<<endl; 132 if(str[i]>str[j]){wise=0;pos=ret1;break;} 133 else if(str[i]<str[j]){wise=1;pos=ret2;break;} 134 } 135 if(wise==-1){ 136 if(ret1<ret2) pos = ret1 , wise = 0; 137 else if(ret1>ret2) pos = ret2 , wise = 1; 138 else pos=ret1 , wise=0; 139 // cout<<"in: "<<endl; 140 } 141 printf("%d %d\n" , pos, wise); 142 } 143 return 0; 144 }
我还在坚持,我还未达到我所想,梦~~一直在