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 }
时间: 2024-10-14 06:20:14

poj 3294的相关文章

POJ 3294 Life Forms (后缀数组)

题目大意: 求出在m个串中出现过大于m/2次的子串. 思路分析: 如果你只是直接跑一次后缀数组,然后二分答案扫描的话. 那么就试一下下面这个数据. 2 abcdabcdefgh efgh 这个数据应该输出 efgh 问题就在于对于每一个串,都只能参与一次计数,所以在check的时候加一个标记数组是正解. #include <cstdio> #include <iostream> #include <algorithm> #include <cstring>

Poj 3294 Life Forms (后缀数组 + 二分 + Hash)

题目链接: Poj 3294 Life Forms 题目描述: 有n个文本串,问在一半以上的文本串出现过的最长连续子串? 解题思路: 可以把文本串用没有出现过的不同字符连起来,然后求新文本串的height.然后二分答案串的长度K,根据K把新文本串的后缀串分块,统计每块中的原文本串出现的次数,大于原文本串数目的一半就作为答案记录下来,对于输出字典序,height就是排好序的后缀数组,只要按照顺序输出即可. 1 #include <cstdio> 2 #include <cstring>

Life Forms POJ - 3294

Life Forms POJ - 3294 题意:给出n个字符串,问出现次数大于n/2的最长的子串(如有多个则都要) 用特殊字符连接字符串,后缀数组搞一下. 二分答案即可 1 /************************************************************************* 2 > File Name: sa.cpp 3 > Author: yijiull 4 > Mail: [email protected] 5 > Create

POJ 3294 后缀数组:求不小于k个字符串中的最长子串

思路:先把所有的串连接成一个串,串写串之前用没出现过的字符隔开,然后求后缀:对height数组分组二分求得最长的公共前缀,公共前缀所在的串一定要是不同的,不然就不是所有串的公共前缀了,然后记下下标和长度即可. 刚开始理解错题意,然后不知道怎么写,然后看别人题解也不知道怎么意思,后面看了好久才知道题目意思理解错了. 时间四千多ms,别人才一百多ms,不知道别人怎么做的-- #include<iostream> #include<cstdio> #include<cstring&

POJ 3294 Life Forms(后缀数组+二分答案)

[题目链接] http://poj.org/problem?id=3294 [题目大意] 求出在至少在一半字符串中出现的最长子串. 如果有多个符合的答案,请按照字典序输出. [题解] 将所有的字符串通过不同的拼接符相连,作一次后缀数组, 二分答案的长度,然后在h数组中分组,判断是否可行, 按照sa扫描输出长度为L的答案即可.注意在一个子串中重复出现答案串的情况. [代码] #include <cstdio> #include <cstring> #include <vecto

POJ 3294 Life Forms [最长公共子串加强版 后缀数组 &amp;&amp; 二分]

题目:http://poj.org/problem?id=3294 Life Forms Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 18549   Accepted: 5454 Description You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial traits s

POJ 3294 出现在至少K个字符串中的子串

在掌握POJ 2774(两个串求最长公共子串)以及对Height数组分组后,本题还是容易想出思路的. 首先用字符集外的不同字符连接所有串,这是为了防止两个后缀在比较时超过某个字符串的分界.二分子串的长度,扫描height数组,判定是否有某个分组来源与至少K个原字符串(本题要求出现超过n的一半次). #include <iostream> #include <vector> #include <algorithm> #include <string> #inc

后缀数组(多个字符串的最长公共子串)—— POJ 3294

对应POJ 题目:点击打开链接 Life Forms Time Limit:6666MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Description Problem C: Life Forms You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial tra

POJ 3294 Life Forms

这道题搞了好久,刚开始数组开小了RE,然后是用了set就TLE,最后用数组替换set就变成了WA,最后终于发现了问题,原来if语句和else语句之间的逻辑没搞清楚 A掉之后感觉眼泪都要掉下来了 再总结一下A题的经验,做不出的题不要一直死磕,只要连续1-2个小时都没什么头绪,就可以先去干干别的事了,甚至可以去做做别的题目,给自己清空一下脑子 等到过段时间再回来做的时候,往往就能解决了 #include<stdio.h> #include<string.h> #include<s

【POJ 3294】Life Forms 不小于k个字符串中的最长子串

一下午和一晚上都在刚这道题,各种错误都集齐了so sad 我的时间啊!!! 后缀数组就先做到这里吧,是在伤不起啊QAQ 出现了各种奇怪的错误,看了标算,然后乱改自己的代码,莫名其妙的改A了,后来发现用字符直接给int赋值会WA,必须一个字符先给另一个字符赋值,后者再给int赋值就能A(什么鬼).后来加了一个(int)s[n]强制转换就简单地A了,评测时强制转换睡觉了吗?还是我rp太差,得多攒点rp #include<cstdio> #include<cstring> #includ