题目大意:

给定一个字符串,可理解成环,然后选定一位置,逆时针或顺时针走一遍,希望得到字典序最大,如果同样大,希望找到起始位置最小的,如果还相同,就默认顺时针

 

比赛一直因为处理最小位置出错,一结束就想明白了。。。真是作孽

这里正向后缀自动机跑一遍最大的,这样得到的位置肯定是最小的

而逆时针最大就反向重建后缀自动机再跑一遍最大的,但这样因为后缀自动机跑出的位置是最小的,但返回来就变成最大的位置了

如果存在更小的位置,那么就相当于可以从开头拎出一段移到末尾,那么必然是产生一个循环节,这个长度可以利用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 }
SAM+kmp

 

 

后来终于受人指点知道了直接在后缀自动机上找到最后一个位置得到的最大字典序怎么求了,这样就不用求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 }
View Code

 

 posted on 2015-09-13 16:33  Love风吟  阅读(304)  评论(0编辑  收藏  举报