poj 3294

一道非常经典的题目 , 求至少在超过一半的字符串中出现过的最长子串 , 并且按字典序删除 , 方法有很多种 , 后缀数组也可以 , 在绝大多数的后缀数组题目中 , 都要用到二分和分段的思想 ,二分长度,然后依据长度k分段 , 分段即把height数组分成多段 , 使得每一段中 , 如果有多个字符串, 那么它们的公共前缀长度至少为k , k就是在那里分段的依据 。 然后对于每一段 , 因为其中字符串的公共前缀长度至少为k , 满足二分的长度, 所以就只需要判断这些后缀来自的字符串的个数是否超过n/2即可。 如果不理解的话, 就自己对照程序,在纸上走几次。

 

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<string>
  6 #include<cmath>
  7 
  8 const int N = 222222 ;
  9 
 10 using namespace std ;
 11 
 12 char Str[N] , s[1010] ;
 13 int sa[N] , rank[N] , height[N] , t1[N] , t2[N] , c[N] , n , t_n;
 14 int s_len[110] , str[N];
 15 bool flag[110] ;
 16 
 17 void build_sa( int m )
 18 {
 19      int *x = t1 , *y = t2 ;
 20      
 21      for( int i=0;i<m;i++ ) c[i] = 0 ;
 22      
 23      for( int i=0;i<n;i++ ) c[ x[i]=str[i] ] ++ ;
 24      
 25      for( int i=1;i<m;i++ ) c[i] += c[i-1] ;
 26      
 27      for( int i=n-1;i>=0;i-- ) sa[ --c[ x[i] ] ] = i ;
 28      
 29      for( int k=1;k<=n;k<<=1 ){
 30               int p = 0 ;
 31               
 32               for( int i=n-k;i<n;i++ ) y[p++] = i ;
 33               
 34               for( int i=0;i<n;i++ ) if( sa[i]>=k ) y[p++] = sa[i] - k ;
 35               
 36               for( int i=0;i<m;i++ ) c[i] = 0 ;
 37               
 38               for( int i=0;i<n;i++ ) c[ x[y[i]] ] ++ ;
 39               
 40               for( int i=1;i<m;i++ ) c[i] += c[i-1] ;
 41               
 42               for( int i=n-1;i>=0;i-- ) sa[ --c[ x[y[i]] ] ] = y[i] ;
 43               
 44               swap( x , y );
 45               
 46               p = 1 ; x[ sa[0] ] = 0 ;
 47               
 48               for( int i=1;i<n;i++ )
 49                        x[ sa[i] ] = y[ sa[i-1] ]==y[ sa[i] ] && y[ sa[i-1]+k ]==y[ sa[i]+k ] ? p-1 : p++ ;
 50                        
 51               if( p>=n ) break ;
 52               
 53               m = p ;    
 54      }
 55 }
 56 
 57 void get_height()
 58 {
 59      for( int i=0;i<n;i++ ) rank[ sa[i] ] = i ;
 60      
 61      int k = 0 ;
 62      
 63      for( int i=0;i<n;i++ ){
 64               if( k ) k-- ;
 65               
 66               int j = sa[ rank[i]-1 ] ;
 67               
 68               while( str[i+k]==str[j+k] ) k++ ;
 69               
 70               height[ rank[i] ] = k ;       
 71      }
 72 }
 73 
 74 int get( int x )
 75 {
 76     x++ ;
 77     for( int i=1;i<=t_n;i++ ){
 78          
 79              x -= s_len[i] ;
 80              
 81              x-- ;
 82              
 83              if( x<0 ) return i ; 
 84              if( x==0 ) return 0 ;    
 85     }
 86     return 0 ;
 87 }
 88 
 89 bool is_ok( int mid )
 90 {
 91      int pre = 1 ;
 92     // cout<<mid<<endl;
 93      for( int i=2;i<n;i++ ){
 94               if( height[i]<mid ){
 95                       if( height[i-1]>=mid ){
 96                           
 97                                 memset( flag , false , sizeof(flag) );
 98                                 for( int j=pre;j<i;j++ ){
 99                                           flag[ get( sa[j] ) ] = true ;      
100                                 }    
101                                 int num = 0 ; 
102                                 for( int j=1;j<=t_n;j++ )
103                                          if( flag[j]==true ) num ++ ;
104                                          
105                                // cout<<num<<" num "<<" "<<endl;
106                                 if( num>t_n/2 ) return true ;
107                       }
108                                 pre = i;   
109               }
110      }
111      return false ;
112 }
113 
114 void print( int mid )
115 {
116      int pre = 1 ;
117      
118      for( int i=2;i<n;i++ ){
119           
120               if( height[i]<mid ){
121                        if( height[i-1]>=mid ){
122                            
123                                memset( flag , false , sizeof(flag) );
124                                 for( int j=pre;j<i;j++ ){
125                                           flag[ get( sa[j] ) ] = true ;      
126                                 }    
127                                 int num = 0 ; 
128                                 for( int j=1;j<=t_n;j++ )
129                                          if( flag[j]==true ) num ++ ;
130                                
131                                 if( num>t_n/2 ){
132                                                    for( int k=0;k<mid && sa[pre]+k<n-1;k++ )  printf("%c" , (char)str[ sa[pre]+k ] );    
133                                                    printf("\n");
134                                 }
135                         }
136                                pre= i ;
137               }     
138      }            
139 }
140 
141 int main()
142 {
143     int jishi = 0 ;
144     
145     while( scanf("%d" , &t_n)!=EOF && t_n )
146     {
147            if( jishi ) printf("\n");
148            jishi ++ ;
149            scanf("%s" , Str);
150            
151            n = strlen(Str) ;
152            
153            for( int i=0;i<n;i++ )
154                     str[i] = (int)Str[i] ;
155            n-- ;
156            
157            s_len[1] = n+1 ;
158            
159            int t = 1 , len ;
160            for( int i=2;i<=t_n;i++ ){
161                     scanf("%s" , s);
162                     
163                     len = strlen(s);
164                     s_len[++t] = len ;
165                     
166                     str[++n] = 127 + i ; 
167                     for( int j=0;j<len;j++ )
168                              str[++n] = s[j] ;     
169            } 
170            
171            str[++n] = 'a' - 2 ;
172            
173            n++ ;
174            
175            build_sa(350) ;
176            
177            get_height() ;
178 
179            int l = 0 , r = n , mid ;
180            
181            while( l<r ){
182                   if( l==r-1 ) break ;  
183                       mid = ( l+r ) >> 1 ;
184                       
185                       if( is_ok(mid)==true )
186                                            l = mid ;
187                       else                 r = mid - 1 ;         
188            }
189            if( is_ok(r)==true ) print(r);
190            else if( l==0 ) printf("?\n");
191            else print(l); 
192     }
193 }

 

  

转载于:https://www.cnblogs.com/Crying-Smile/p/4109184.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值