一道非常经典的题目 , 求至少在超过一半的字符串中出现过的最长子串 , 并且按字典序删除 , 方法有很多种 , 后缀数组也可以 , 在绝大多数的后缀数组题目中 , 都要用到二分和分段的思想 ,二分长度,然后依据长度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 }
时间: 2024-10-14 06:20:14