• POJ


    多个字符串的相关问题

    这类问题的一个常用做法是,先将所有的字符串连接起来,

    然后求后缀数组 和 height 数组,再利用 height 数组进行求解。

    这中间可能需要二分答案。

    POJ - 3294

    题意:

    给出n个串,求至少出现在n/2+1个串中的最长公共子串 

    题解:

    (摘自罗穗骞的国家集训队论文):

    将 n 个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开, 求后缀数组。

    然后二分答案,用和LCP将后缀分成若干组,判断每组的后缀是否出现在不小于 k 个的原串中。

    这个做法的时间复杂度为 O(nlogn)。

    你需要用一个id数组,记录S串的每个位置对于这哪一个串,然后二分每次用vis数组标记,查看那几个串出现过。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <queue>
      4 #include <cmath>
      5 #include <algorithm>
      6 #include <set>
      7 #include <iostream>
      8 #include <map>
      9 #include <stack>
     10 #include <string>
     11 #include <time.h>
     12 #include <vector>
     13 #define  pi acos(-1.0)
     14 #define  eps 1e-9
     15 #define  fi first
     16 #define  se second
     17 #define  rtl   rt<<1
     18 #define  rtr   rt<<1|1
     19 #define  bug         printf("******
    ")
     20 #define  mem(a,b)    memset(a,b,sizeof(a))
     21 #define  name2str(x) #x
     22 #define  fuck(x)     cout<<#x" = "<<x<<endl
     23 #define  f(a)        a*a
     24 #define  sf(n)       scanf("%d", &n)
     25 #define  sff(a,b)    scanf("%d %d", &a, &b)
     26 #define  sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
     27 #define  sffff(a,b,c,d) scanf("%d %d %d %d", &a, &b, &c, &d)
     28 #define  pf          printf
     29 #define  FRE(i,a,b)  for(i = a; i <= b; i++)
     30 #define  FREE(i,a,b) for(i = a; i >= b; i--)
     31 #define  FRL(i,a,b)  for(i = a; i < b; i++)+
     32 #define  FRLL(i,a,b) for(i = a; i > b; i--)
     33 #define  FIN         freopen("data.txt","r",stdin)
     34 #define  gcd(a,b)    __gcd(a,b)
     35 #define  lowbit(x)   x&-x
     36 #define rep(i,a,b) for(int i=a;i<b;++i)
     37 #define per(i,a,b) for(int i=a-1;i>=b;--i)
     38 
     39 using namespace std;
     40 typedef long long  LL;
     41 typedef unsigned long long ULL;
     42 const int maxn = 2e6 + 7;
     43 const int maxm = 8e6 + 10;
     44 const int INF = 0x3f3f3f3f;
     45 const int mod = 10007;
     46 
     47 //rnk从0开始
     48 //sa从1开始,因为最后一个字符(最小的)排在第0位
     49 //height从1开始,因为表示的是sa[i - 1]和sa[i]
     50 //倍增算法 O(nlogn)
     51 int wa[maxn], wb[maxn], wv[maxn], ws_[maxn];
     52 int Rank[maxn], height[maxn], sa[maxn], r[maxn];
     53 int n, maxx;
     54 char s[maxn];
     55 //Suffix函数的参数m代表字符串中字符的取值范围,是基数排序的一个参数,如果原序列都是字母可以直接取128,如果原序列本身都是整数的话,则m可以取比最大的整数大1的值
     56 //待排序的字符串放在r数组中,从r[0]到r[n-1],长度为n
     57 //为了方便比较大小,可以在字符串后面添加一个字符,这个字符没有在前面的字符中出现过,而且比前面的字符都要小
     58 //同上,为了函数操作的方便,约定除r[n-1]外所有的r[i]都大于0,r[n-1]=0
     59 //函数结束后,结果放在sa数组中,从sa[0]到sa[n-1]
     60 void Suffix ( int *r, int *sa, int n, int m ) {
     61     int i, j, k, *x = wa, *y = wb, *t;
     62     //对长度为1的字符串排序
     63     //一般来说,在字符串的题目中,r的最大值不会很大,所以这里使用了基数排序
     64     //如果r的最大值很大,那么把这段代码改成快速排序
     65     for ( i = 0; i < m; ++i ) ws_[i] = 0;
     66     for ( i = 0; i < n; ++i ) ws_[x[i] = r[i]]++; //统计字符的个数
     67     for ( i = 1; i < m; ++i ) ws_[i] += ws_[i - 1]; //统计不大于字符i的字符个数
     68     for ( i = n - 1; i >= 0; --i ) sa[--ws_[x[i]]] = i; //计算字符排名
     69     //基数排序
     70     //x数组保存的值相当于是rank值
     71     for ( j = 1, k = 1; k < n; j *= 2, m = k ) {
     72         //j是当前字符串的长度,数组y保存的是对第二关键字排序的结果
     73         //第二关键字排序
     74         for ( k = 0, i = n - j; i < n; ++i ) y[k++] = i; //第二关键字为0的排在前面
     75         for ( i = 0; i < n; ++i ) if ( sa[i] >= j ) y[k++] = sa[i] - j; //长度为j的子串sa[i]应该是长度为2 * j的子串sa[i] - j的后缀(第二关键字),对所有的长度为2 * j的子串根据第二关键字来排序
     76         for ( i = 0; i < n; ++i ) wv[i] = x[y[i]]; //提取第一关键字
     77         //按第一关键字排序 (原理同对长度为1的字符串排序)
     78         for ( i = 0; i < m; ++i ) ws_[i] = 0;
     79         for ( i = 0; i < n; ++i ) ws_[wv[i]]++;
     80         for ( i = 1; i < m; ++i ) ws_[i] += ws_[i - 1];
     81         for ( i = n - 1; i >= 0; --i ) sa[--ws_[wv[i]]] = y[i]; //按第一关键字,计算出了长度为2 * j的子串排名情况
     82         //此时数组x是长度为j的子串的排名情况,数组y仍是根据第二关键字排序后的结果
     83         //计算长度为2 * j的子串的排名情况,保存到数组x
     84         t = x;
     85         x = y;
     86         y = t;
     87         for ( x[sa[0]] = 0, i = k = 1; i < n; ++i )
     88             x[sa[i]] = ( y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j] ) ? k - 1 : k++;
     89         //若长度为2 * j的子串sa[i]与sa[i - 1]完全相同,则他们有相同的排名
     90     }
     91 }
     92 void calheight ( int *r, int *sa, int n ) {
     93     int i, j, k = 0;
     94     for ( i = 1; i <= n; i++ ) Rank[sa[i]] = i;
     95     for ( i = 0; i < n; height[Rank[i++]] = k )
     96         for ( k ? k-- : 0, j = sa[Rank[i] - 1]; r[i + k] == r[j + k]; k++ );
     97 }
     98 int num, id[maxn], vis[105], ans[maxn], sum;
     99 int check ( int mid ) {
    100     int cnt = 0;
    101     sum = 0;
    102     mem ( vis, 0 );
    103     for ( int i = 2 ; i <= n ; i++ ) {
    104         if ( height[i] < mid ) {
    105             if ( cnt >= num / 2 + 1 ) ans[++sum] = sa[i-1];
    106             mem ( vis, 0 );
    107             cnt = 0;
    108         } else {
    109             if ( !vis[id[sa[i]]] ) cnt++, vis[id[sa[i]]] = 1;
    110             if ( !vis[id[sa[i - 1]]] ) cnt++, vis[id[sa[i - 1]]] = 1;
    111         }
    112     }
    113     if ( cnt >= num / 2 + 1 ) ans[++sum] = sa[n];
    114     if ( sum ) return 1;
    115     return 0;
    116 }
    117 int main() {
    118     int flag=0;
    119     while ( ~sf ( num ) && num ) {
    120         if (flag) printf("
    ");
    121         else flag=1;
    122         n = 0;
    123         int low = 1, high = INF;
    124         for ( int i = 1 ; i <= num ; i++ ) {
    125             scanf ( "%s", s );
    126             int len = strlen ( s );
    127             high = min ( high, len );
    128             for ( int j = 0 ; j < len ; j++ ) {
    129                 r[n + j] = s[j] + 101;
    130                 maxx = max ( maxx, r[n + j] );
    131                 id[n + j] = i;
    132             }
    133             r[n + len] = i;
    134             id[n + len] = 0;
    135             n += len + 1;
    136         }
    137         r[n] = 0;
    138 //        for ( int i = 0; i <= n ; i++ ) printf ( "%d%c", r[i], ( i == n ? '
    ' : ' ' ) );
    139 //        for ( int i = 0; i <= n ; i++ ) printf ( "%d%c", id[i], ( i == n ? '
    ' : ' ' ) );
    140         Suffix ( r, sa, n + 1, maxx + 1 );
    141         calheight ( r, sa, n );
    142         int maxlen = 0;
    143         while ( low <= high ) {
    144             int mid = ( low + high ) / 2;
    145             if ( check ( mid ) ) maxlen = mid, low = mid + 1;
    146             else high = mid - 1;
    147         }
    148         check ( maxlen );
    149 //        fuck ( sum );
    150 //        fuck ( maxlen );
    151         if ( maxlen == 0 ) {
    152             printf ( "?
    " );
    153             continue;
    154         }
    155         for ( int i = 1 ; i <= sum ; i++ ) {
    156 //            fuck ( ans[i] );
    157             for ( int j = ans[i] ; j < ans[i] + maxlen ; j++ ) printf ( "%c", r[j] - 101 );
    158             printf ( "
    " );
    159         }
    160     }
    161     return 0;
    162 }
    View Code

    Relevant Phrases of Annihilation SPOJ - PHRASES 

    题意:求每个字符串至少出现两次的且不重叠的最长公共子串。

    题解:

    这题的处理和上一题差不多,串的数目只有10;

    通过后缀数组常用套路 二分+判定过题。

    先通过常用的LCP分组,

    然后在枚举的过程中,记录每一个字串在原串最早出现的位置和最晚的出现的位置。

    判断的时候判定R-L>=mid

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <queue>
      4 #include <cmath>
      5 #include <algorithm>
      6 #include <set>
      7 #include <iostream>
      8 #include <map>
      9 #include <stack>
     10 #include <string>
     11 #include <time.h>
     12 #include <vector>
     13 #define  pi acos(-1.0)
     14 #define  eps 1e-9
     15 #define  fi first
     16 #define  se second
     17 #define  rtl   rt<<1
     18 #define  rtr   rt<<1|1
     19 #define  bug         printf("******
    ")
     20 #define  mem(a,b)    memset(a,b,sizeof(a))
     21 #define  name2str(x) #x
     22 #define  fuck(x)     cout<<#x" = "<<x<<endl
     23 #define  f(a)        a*a
     24 #define  sf(n)       scanf("%d", &n)
     25 #define  sff(a,b)    scanf("%d %d", &a, &b)
     26 #define  sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
     27 #define  sffff(a,b,c,d) scanf("%d %d %d %d", &a, &b, &c, &d)
     28 #define  pf          printf
     29 #define  FRE(i,a,b)  for(i = a; i <= b; i++)
     30 #define  FREE(i,a,b) for(i = a; i >= b; i--)
     31 #define  FRL(i,a,b)  for(i = a; i < b; i++)+
     32 #define  FRLL(i,a,b) for(i = a; i > b; i--)
     33 #define  FIN         freopen("data.txt","r",stdin)
     34 #define  gcd(a,b)    __gcd(a,b)
     35 #define  lowbit(x)   x&-x
     36 #define rep(i,a,b) for(int i=a;i<b;++i)
     37 #define per(i,a,b) for(int i=a-1;i>=b;--i)
     38 
     39 using namespace std;
     40 typedef long long  LL;
     41 typedef unsigned long long ULL;
     42 const int maxn = 1e6 + 7;
     43 const int maxm = 8e6 + 10;
     44 const int INF = 0x3f3f3f3f;
     45 const int mod = 10007;
     46 
     47 //rnk从0开始
     48 //sa从1开始,因为最后一个字符(最小的)排在第0位
     49 //height从1开始,因为表示的是sa[i - 1]和sa[i]
     50 //倍增算法 O(nlogn)
     51 int wa[maxn], wb[maxn], wv[maxn], ws_[maxn];
     52 int Rank[maxn], height[maxn], sa[maxn], r[maxn];
     53 int n, maxx;
     54 char s[maxn];
     55 //Suffix函数的参数m代表字符串中字符的取值范围,是基数排序的一个参数,如果原序列都是字母可以直接取128,如果原序列本身都是整数的话,则m可以取比最大的整数大1的值
     56 //待排序的字符串放在r数组中,从r[0]到r[n-1],长度为n
     57 //为了方便比较大小,可以在字符串后面添加一个字符,这个字符没有在前面的字符中出现过,而且比前面的字符都要小
     58 //同上,为了函数操作的方便,约定除r[n-1]外所有的r[i]都大于0,r[n-1]=0
     59 //函数结束后,结果放在sa数组中,从sa[0]到sa[n-1]
     60 void Suffix ( int *r, int *sa, int n, int m ) {
     61     int i, j, k, *x = wa, *y = wb, *t;
     62     //对长度为1的字符串排序
     63     //一般来说,在字符串的题目中,r的最大值不会很大,所以这里使用了基数排序
     64     //如果r的最大值很大,那么把这段代码改成快速排序
     65     for ( i = 0; i < m; ++i ) ws_[i] = 0;
     66     for ( i = 0; i < n; ++i ) ws_[x[i] = r[i]]++; //统计字符的个数
     67     for ( i = 1; i < m; ++i ) ws_[i] += ws_[i - 1]; //统计不大于字符i的字符个数
     68     for ( i = n - 1; i >= 0; --i ) sa[--ws_[x[i]]] = i; //计算字符排名
     69     //基数排序
     70     //x数组保存的值相当于是rank值
     71     for ( j = 1, k = 1; k < n; j *= 2, m = k ) {
     72         //j是当前字符串的长度,数组y保存的是对第二关键字排序的结果
     73         //第二关键字排序
     74         for ( k = 0, i = n - j; i < n; ++i ) y[k++] = i; //第二关键字为0的排在前面
     75         for ( i = 0; i < n; ++i ) if ( sa[i] >= j ) y[k++] = sa[i] - j; //长度为j的子串sa[i]应该是长度为2 * j的子串sa[i] - j的后缀(第二关键字),对所有的长度为2 * j的子串根据第二关键字来排序
     76         for ( i = 0; i < n; ++i ) wv[i] = x[y[i]]; //提取第一关键字
     77         //按第一关键字排序 (原理同对长度为1的字符串排序)
     78         for ( i = 0; i < m; ++i ) ws_[i] = 0;
     79         for ( i = 0; i < n; ++i ) ws_[wv[i]]++;
     80         for ( i = 1; i < m; ++i ) ws_[i] += ws_[i - 1];
     81         for ( i = n - 1; i >= 0; --i ) sa[--ws_[wv[i]]] = y[i]; //按第一关键字,计算出了长度为2 * j的子串排名情况
     82         //此时数组x是长度为j的子串的排名情况,数组y仍是根据第二关键字排序后的结果
     83         //计算长度为2 * j的子串的排名情况,保存到数组x
     84         t = x;
     85         x = y;
     86         y = t;
     87         for ( x[sa[0]] = 0, i = k = 1; i < n; ++i )
     88             x[sa[i]] = ( y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j] ) ? k - 1 : k++;
     89         //若长度为2 * j的子串sa[i]与sa[i - 1]完全相同,则他们有相同的排名
     90     }
     91 }
     92 void calheight ( int *r, int *sa, int n ) {
     93     int i, j, k = 0;
     94     for ( i = 1; i <= n; i++ ) Rank[sa[i]] = i;
     95     for ( i = 0; i < n; height[Rank[i++]] = k )
     96         for ( k ? k-- : 0, j = sa[Rank[i] - 1]; r[i + k] == r[j + k]; k++ );
     97 }
     98 
     99 int num, id[maxn], vis[105];
    100 struct node {
    101     int L, R, flag;
    102 } qu[12];
    103 int check ( int mid ) {
    104     mem ( vis, 0 );
    105     for ( int i = 2 ; i <= n ; i++ ) {
    106         if ( height[i] < mid ) {
    107             for ( int i = 1 ; i <= num ; i++ ) qu[i].L = INF, qu[i].R = -INF;
    108         } else {
    109             int idx1 = id[sa[i]], idx2 = id[sa[i - 1]];
    110             qu[idx1].L = min ( qu[idx1].L, sa[i] );
    111             qu[idx1].R = max ( qu[idx1].R, sa[i] );
    112             qu[idx2].L = min ( qu[idx2].L, sa[i - 1] );
    113             qu[idx2].R = max ( qu[idx2].R, sa[i - 1] );
    114             int cnt = 0;
    115             for ( int i = 1 ; i <= num ; i++ ) if ( qu[i].R - qu[i].L >= mid ) cnt++;
    116             if ( cnt == num ) return 1;
    117         }
    118     }
    119     return 0;
    120 }
    121 int T;
    122 int main() {
    123     sf ( T );
    124     while ( T--  ) {
    125         sf ( num );
    126         n = 0;
    127         int low = 1, high = INF;
    128         for ( int i = 1 ; i <= num ; i++ ) {
    129             scanf ( "%s", s );
    130             int len = strlen ( s );
    131             high = min ( high, len );
    132             for ( int j = 0 ; j < len ; j++ ) {
    133                 r[n + j] = s[j] + 101;
    134                 maxx = max ( maxx, r[n + j] );
    135                 id[n + j] = i;
    136             }
    137             r[n + len] = i;
    138             id[n + len] = 0;
    139             n += len + 1;
    140         }
    141         r[n] = 0;
    142 //        for ( int i = 0; i <= n ; i++ ) printf ( "%d%c", r[i], ( i == n ? '
    ' : ' ' ) );
    143 //        for ( int i = 0; i <= n ; i++ ) printf ( "%d%c", id[i], ( i == n ? '
    ' : ' ' ) );
    144         Suffix ( r, sa, n + 1, maxx + 1 );
    145         calheight ( r, sa, n );
    146 
    147         int maxlen = 0;
    148         while ( low <= high ) {
    149             int mid = ( low + high ) / 2;
    150             if ( check ( mid ) ) maxlen = mid, low = mid + 1;
    151             else high = mid - 1;
    152         }
    153         printf ( "%d
    ", maxlen );
    154     }
    155     return 0;
    156 }
    View Code

    POJ - 1226

    题目:出现或反转后出现在每个字符串中的最长公共子串。

    题解:

    做过多个后缀数组题目之后,处理就很显然了。

    将所有串的正串和反串拼接在一起,然后用上面的套路,二分判断就AC了。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <queue>
      4 #include <cmath>
      5 #include <algorithm>
      6 #include <set>
      7 #include <iostream>
      8 #include <map>
      9 #include <stack>
     10 #include <string>
     11 #include <time.h>
     12 #include <vector>
     13 #define  pi acos(-1.0)
     14 #define  eps 1e-9
     15 #define  fi first
     16 #define  se second
     17 #define  rtl   rt<<1
     18 #define  rtr   rt<<1|1
     19 #define  bug         printf("******
    ")
     20 #define  mem(a,b)    memset(a,b,sizeof(a))
     21 #define  name2str(x) #x
     22 #define  fuck(x)     cout<<#x" = "<<x<<endl
     23 #define  f(a)        a*a
     24 #define  sf(n)       scanf("%d", &n)
     25 #define  sff(a,b)    scanf("%d %d", &a, &b)
     26 #define  sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
     27 #define  sffff(a,b,c,d) scanf("%d %d %d %d", &a, &b, &c, &d)
     28 #define  pf          printf
     29 #define  FRE(i,a,b)  for(i = a; i <= b; i++)
     30 #define  FREE(i,a,b) for(i = a; i >= b; i--)
     31 #define  FRL(i,a,b)  for(i = a; i < b; i++)+
     32 #define  FRLL(i,a,b) for(i = a; i > b; i--)
     33 #define  FIN         freopen("data.txt","r",stdin)
     34 #define  gcd(a,b)    __gcd(a,b)
     35 #define  lowbit(x)   x&-x
     36 #define rep(i,a,b) for(int i=a;i<b;++i)
     37 #define per(i,a,b) for(int i=a-1;i>=b;--i)
     38 
     39 using namespace std;
     40 typedef long long  LL;
     41 typedef unsigned long long ULL;
     42 const int maxn = 1e6 + 7;
     43 const int maxm = 8e6 + 10;
     44 const int INF = 0x3f3f3f3f;
     45 const int mod = 10007;
     46 
     47 //rnk从0开始
     48 //sa从1开始,因为最后一个字符(最小的)排在第0位
     49 //height从1开始,因为表示的是sa[i - 1]和sa[i]
     50 //倍增算法 O(nlogn)
     51 int wa[maxn], wb[maxn], wv[maxn], ws_[maxn];
     52 int Rank[maxn], height[maxn], sa[maxn], r[maxn];
     53 int n, maxx;
     54 char s[maxn];
     55 //Suffix函数的参数m代表字符串中字符的取值范围,是基数排序的一个参数,如果原序列都是字母可以直接取128,如果原序列本身都是整数的话,则m可以取比最大的整数大1的值
     56 //待排序的字符串放在r数组中,从r[0]到r[n-1],长度为n
     57 //为了方便比较大小,可以在字符串后面添加一个字符,这个字符没有在前面的字符中出现过,而且比前面的字符都要小
     58 //同上,为了函数操作的方便,约定除r[n-1]外所有的r[i]都大于0,r[n-1]=0
     59 //函数结束后,结果放在sa数组中,从sa[0]到sa[n-1]
     60 void Suffix ( int *r, int *sa, int n, int m ) {
     61     int i, j, k, *x = wa, *y = wb, *t;
     62     //对长度为1的字符串排序
     63     //一般来说,在字符串的题目中,r的最大值不会很大,所以这里使用了基数排序
     64     //如果r的最大值很大,那么把这段代码改成快速排序
     65     for ( i = 0; i < m; ++i ) ws_[i] = 0;
     66     for ( i = 0; i < n; ++i ) ws_[x[i] = r[i]]++; //统计字符的个数
     67     for ( i = 1; i < m; ++i ) ws_[i] += ws_[i - 1]; //统计不大于字符i的字符个数
     68     for ( i = n - 1; i >= 0; --i ) sa[--ws_[x[i]]] = i; //计算字符排名
     69     //基数排序
     70     //x数组保存的值相当于是rank值
     71     for ( j = 1, k = 1; k < n; j *= 2, m = k ) {
     72         //j是当前字符串的长度,数组y保存的是对第二关键字排序的结果
     73         //第二关键字排序
     74         for ( k = 0, i = n - j; i < n; ++i ) y[k++] = i; //第二关键字为0的排在前面
     75         for ( i = 0; i < n; ++i ) if ( sa[i] >= j ) y[k++] = sa[i] - j; //长度为j的子串sa[i]应该是长度为2 * j的子串sa[i] - j的后缀(第二关键字),对所有的长度为2 * j的子串根据第二关键字来排序
     76         for ( i = 0; i < n; ++i ) wv[i] = x[y[i]]; //提取第一关键字
     77         //按第一关键字排序 (原理同对长度为1的字符串排序)
     78         for ( i = 0; i < m; ++i ) ws_[i] = 0;
     79         for ( i = 0; i < n; ++i ) ws_[wv[i]]++;
     80         for ( i = 1; i < m; ++i ) ws_[i] += ws_[i - 1];
     81         for ( i = n - 1; i >= 0; --i ) sa[--ws_[wv[i]]] = y[i]; //按第一关键字,计算出了长度为2 * j的子串排名情况
     82         //此时数组x是长度为j的子串的排名情况,数组y仍是根据第二关键字排序后的结果
     83         //计算长度为2 * j的子串的排名情况,保存到数组x
     84         t = x;
     85         x = y;
     86         y = t;
     87         for ( x[sa[0]] = 0, i = k = 1; i < n; ++i )
     88             x[sa[i]] = ( y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j] ) ? k - 1 : k++;
     89         //若长度为2 * j的子串sa[i]与sa[i - 1]完全相同,则他们有相同的排名
     90     }
     91 }
     92 void calheight ( int *r, int *sa, int n ) {
     93     int i, j, k = 0;
     94     for ( i = 1; i <= n; i++ ) Rank[sa[i]] = i;
     95     for ( i = 0; i < n; height[Rank[i++]] = k )
     96         for ( k ? k-- : 0, j = sa[Rank[i] - 1]; r[i + k] == r[j + k]; k++ );
     97 }
     98 
     99 int num, id[maxn], vis[105];
    100 int check ( int mid ) {
    101     int cnt = 0;
    102     mem ( vis, 0 );
    103     for ( int i = 2 ; i <= n ; i++ ) {
    104         if ( height[i] < mid ) {
    105             if ( cnt >= num ) return 1;
    106             mem ( vis, 0 );
    107             cnt = 0;
    108         } else {
    109             if ( !vis[id[sa[i]]] ) vis[id[sa[i]]] = 1, cnt++;
    110             if ( !vis[id[sa[i - 1]]] ) vis[id[sa[i - 1]]] = 1, cnt++;
    111         }
    112     }
    113     if ( cnt >= num ) return 1;
    114     return 0;
    115 }
    116 int T;
    117 int main() {
    118     sf ( T );
    119     while ( T--  ) {
    120         sf ( num );
    121         n = 0;
    122         int low = 1, high = INF;
    123         for ( int i = 1 ; i <= num ; i++ ) {
    124             scanf ( "%s", s );
    125             int len = strlen ( s );
    126             high = min ( high, len );
    127             for ( int j = 0 ; j < len ; j++ ) {
    128                 r[n + j] = s[j] + 201;
    129                 maxx = max ( maxx, r[n + j] );
    130                 id[n + j] = i;
    131             }
    132             r[n + len] = i;
    133             id[n + len] = 0;
    134             n += len + 1;
    135             reverse ( s, s + len );
    136             for ( int j = 0 ; j < len ; j++ ) {
    137                 r[n + j] = s[j] + 201;
    138                 id[n + j] = i;
    139             }
    140             r[n + len] = num + i;
    141             id[n + len] = 0;
    142             n += len + 1;
    143         }
    144         r[n] = 0;
    145 //        for ( int i = 0; i <= n ; i++ ) printf ( "%d%c", r[i], ( i == n ? '
    ' : ' ' ) );
    146 //        for ( int i = 0; i <= n ; i++ ) printf ( "%d%c", id[i], ( i == n ? '
    ' : ' ' ) );
    147         Suffix ( r, sa, n + 1, maxx + 1 );
    148         calheight ( r, sa, n );
    149 
    150         int maxlen = 0;
    151         while ( low <= high ) {
    152             int mid = ( low + high ) / 2;
    153             if ( check ( mid ) ) maxlen = mid, low = mid + 1;
    154             else high = mid - 1;
    155         }
    156         printf ( "%d
    ", maxlen );
    157     }
    158     return 0;
    159 }
    View Code

    POJ - 3450

    题意:

    求n个字符串的最长公共子串长度 

    题解:

    和上题的处理几乎一摸一样

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <queue>
      4 #include <cmath>
      5 #include <algorithm>
      6 #include <set>
      7 #include <iostream>
      8 #include <map>
      9 #include <stack>
     10 #include <string>
     11 #include <time.h>
     12 #include <vector>
     13 #define  pi acos(-1.0)
     14 #define  eps 1e-9
     15 #define  fi first
     16 #define  se second
     17 #define  rtl   rt<<1
     18 #define  rtr   rt<<1|1
     19 #define  bug         printf("******
    ")
     20 #define  mem(a,b)    memset(a,b,sizeof(a))
     21 #define  name2str(x) #x
     22 #define  fuck(x)     cout<<#x" = "<<x<<endl
     23 #define  f(a)        a*a
     24 #define  sf(n)       scanf("%d", &n)
     25 #define  sff(a,b)    scanf("%d %d", &a, &b)
     26 #define  sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
     27 #define  sffff(a,b,c,d) scanf("%d %d %d %d", &a, &b, &c, &d)
     28 #define  pf          printf
     29 #define  FRE(i,a,b)  for(i = a; i <= b; i++)
     30 #define  FREE(i,a,b) for(i = a; i >= b; i--)
     31 #define  FRL(i,a,b)  for(i = a; i < b; i++)+
     32 #define  FRLL(i,a,b) for(i = a; i > b; i--)
     33 #define  FIN         freopen("data.txt","r",stdin)
     34 #define  gcd(a,b)    __gcd(a,b)
     35 #define  lowbit(x)   x&-x
     36 #define rep(i,a,b) for(int i=a;i<b;++i)
     37 #define per(i,a,b) for(int i=a-1;i>=b;--i)
     38 
     39 using namespace std;
     40 typedef long long  LL;
     41 typedef unsigned long long ULL;
     42 const int maxn = 1e6 + 7;
     43 const int maxm = 8e6 + 10;
     44 const int INF = 0x3f3f3f3f;
     45 const int mod = 10007;
     46 
     47 //rnk从0开始
     48 //sa从1开始,因为最后一个字符(最小的)排在第0位
     49 //height从1开始,因为表示的是sa[i - 1]和sa[i]
     50 //倍增算法 O(nlogn)
     51 int wa[maxn], wb[maxn], wv[maxn], ws_[maxn];
     52 int Rank[maxn], height[maxn], sa[maxn], r[maxn];
     53 int n, maxx;
     54 char s[maxn];
     55 //Suffix函数的参数m代表字符串中字符的取值范围,是基数排序的一个参数,如果原序列都是字母可以直接取128,如果原序列本身都是整数的话,则m可以取比最大的整数大1的值
     56 //待排序的字符串放在r数组中,从r[0]到r[n-1],长度为n
     57 //为了方便比较大小,可以在字符串后面添加一个字符,这个字符没有在前面的字符中出现过,而且比前面的字符都要小
     58 //同上,为了函数操作的方便,约定除r[n-1]外所有的r[i]都大于0,r[n-1]=0
     59 //函数结束后,结果放在sa数组中,从sa[0]到sa[n-1]
     60 void Suffix ( int *r, int *sa, int n, int m ) {
     61     int i, j, k, *x = wa, *y = wb, *t;
     62     //对长度为1的字符串排序
     63     //一般来说,在字符串的题目中,r的最大值不会很大,所以这里使用了基数排序
     64     //如果r的最大值很大,那么把这段代码改成快速排序
     65     for ( i = 0; i < m; ++i ) ws_[i] = 0;
     66     for ( i = 0; i < n; ++i ) ws_[x[i] = r[i]]++; //统计字符的个数
     67     for ( i = 1; i < m; ++i ) ws_[i] += ws_[i - 1]; //统计不大于字符i的字符个数
     68     for ( i = n - 1; i >= 0; --i ) sa[--ws_[x[i]]] = i; //计算字符排名
     69     //基数排序
     70     //x数组保存的值相当于是rank值
     71     for ( j = 1, k = 1; k < n; j *= 2, m = k ) {
     72         //j是当前字符串的长度,数组y保存的是对第二关键字排序的结果
     73         //第二关键字排序
     74         for ( k = 0, i = n - j; i < n; ++i ) y[k++] = i; //第二关键字为0的排在前面
     75         for ( i = 0; i < n; ++i ) if ( sa[i] >= j ) y[k++] = sa[i] - j; //长度为j的子串sa[i]应该是长度为2 * j的子串sa[i] - j的后缀(第二关键字),对所有的长度为2 * j的子串根据第二关键字来排序
     76         for ( i = 0; i < n; ++i ) wv[i] = x[y[i]]; //提取第一关键字
     77         //按第一关键字排序 (原理同对长度为1的字符串排序)
     78         for ( i = 0; i < m; ++i ) ws_[i] = 0;
     79         for ( i = 0; i < n; ++i ) ws_[wv[i]]++;
     80         for ( i = 1; i < m; ++i ) ws_[i] += ws_[i - 1];
     81         for ( i = n - 1; i >= 0; --i ) sa[--ws_[wv[i]]] = y[i]; //按第一关键字,计算出了长度为2 * j的子串排名情况
     82         //此时数组x是长度为j的子串的排名情况,数组y仍是根据第二关键字排序后的结果
     83         //计算长度为2 * j的子串的排名情况,保存到数组x
     84         t = x;
     85         x = y;
     86         y = t;
     87         for ( x[sa[0]] = 0, i = k = 1; i < n; ++i )
     88             x[sa[i]] = ( y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j] ) ? k - 1 : k++;
     89         //若长度为2 * j的子串sa[i]与sa[i - 1]完全相同,则他们有相同的排名
     90     }
     91 }
     92 void calheight ( int *r, int *sa, int n ) {
     93     int i, j, k = 0;
     94     for ( i = 1; i <= n; i++ ) Rank[sa[i]] = i;
     95     for ( i = 0; i < n; height[Rank[i++]] = k )
     96         for ( k ? k-- : 0, j = sa[Rank[i] - 1]; r[i + k] == r[j + k]; k++ );
     97 }
     98 
     99 int num, id[maxn], vis[4005], idx;
    100 int check ( int mid ) {
    101     int cnt = 0;
    102     mem ( vis, 0 );
    103     for ( int i = 2 ; i <= n ; i++ ) {
    104         if ( height[i] < mid ) {
    105             if ( cnt >= num ) {
    106                 idx = sa[i - 1];
    107                 return 1;
    108             }
    109             mem ( vis, 0 );
    110             cnt = 0;
    111         } else {
    112             if ( !vis[id[sa[i]]] ) vis[id[sa[i]]] = 1, cnt++;
    113             if ( !vis[id[sa[i - 1]]] ) vis[id[sa[i - 1]]] = 1, cnt++;
    114         }
    115     }
    116     if ( cnt >= num ) {
    117         idx = s[n];
    118         return 1;
    119     }
    120     return 0;
    121 }
    122 int main() {
    123     while ( sf ( num ) && num ) {
    124         n = 0;
    125         int low = 1, high = INF;
    126         for ( int i = 1 ; i <= num ; i++ ) {
    127             scanf ( "%s", s );
    128             int len = strlen ( s );
    129             high = min ( high, len );
    130             for ( int j = 0 ; j < len ; j++ ) {
    131                 r[n + j] = s[j] + 4001;
    132                 maxx = max ( maxx, r[n + j] );
    133                 id[n + j] = i;
    134             }
    135             r[n + len] = i;
    136             id[n + len] = 0;
    137             n += len + 1;
    138         }
    139         r[n] = 0;
    140 //        for ( int i = 0; i <= n ; i++ ) printf ( "%d%c", r[i], ( i == n ? '
    ' : ' ' ) );
    141 //        for ( int i = 0; i <= n ; i++ ) printf ( "%d%c", id[i], ( i == n ? '
    ' : ' ' ) );
    142         Suffix ( r, sa, n + 1, maxx + 1 );
    143         calheight ( r, sa, n );
    144 
    145         int maxlen = 0;
    146         while ( low <= high ) {
    147             int mid = ( low + high ) / 2;
    148             if ( check ( mid ) ) maxlen = mid, low = mid + 1;
    149             else high = mid - 1;
    150         }
    151         check ( maxlen );
    152 //        fuck(maxlen);
    153         if ( maxlen )  {
    154             for ( int i = idx ; i < idx + maxlen ; i++ ) printf ( "%c", r[i]-4001 );
    155 //            printf ( "%d
    ", maxlen );
    156             printf ( "
    " );
    157         } else printf ( "IDENTITY LOST
    " );
    158     }
    159     return 0;
    160 }
    View Code

     POJ - 3080

    题意:

    找出n个串中的最长公共子串,若存在多个的话输出最小的那一个。

    题解:

    和上上题的处理一样,只是加了一个输出方案而已。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <queue>
      4 #include <cmath>
      5 #include <algorithm>
      6 #include <set>
      7 #include <iostream>
      8 #include <map>
      9 #include <stack>
     10 #include <string>
     11 #include <time.h>
     12 #include <vector>
     13 #define  pi acos(-1.0)
     14 #define  eps 1e-9
     15 #define  fi first
     16 #define  se second
     17 #define  rtl   rt<<1
     18 #define  rtr   rt<<1|1
     19 #define  bug         printf("******
    ")
     20 #define  mem(a,b)    memset(a,b,sizeof(a))
     21 #define  name2str(x) #x
     22 #define  fuck(x)     cout<<#x" = "<<x<<endl
     23 #define  f(a)        a*a
     24 #define  sf(n)       scanf("%d", &n)
     25 #define  sff(a,b)    scanf("%d %d", &a, &b)
     26 #define  sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
     27 #define  sffff(a,b,c,d) scanf("%d %d %d %d", &a, &b, &c, &d)
     28 #define  pf          printf
     29 #define  FRE(i,a,b)  for(i = a; i <= b; i++)
     30 #define  FREE(i,a,b) for(i = a; i >= b; i--)
     31 #define  FRL(i,a,b)  for(i = a; i < b; i++)+
     32 #define  FRLL(i,a,b) for(i = a; i > b; i--)
     33 #define  FIN         freopen("data.txt","r",stdin)
     34 #define  gcd(a,b)    __gcd(a,b)
     35 #define  lowbit(x)   x&-x
     36 #define rep(i,a,b) for(int i=a;i<b;++i)
     37 #define per(i,a,b) for(int i=a-1;i>=b;--i)
     38 
     39 using namespace std;
     40 typedef long long  LL;
     41 typedef unsigned long long ULL;
     42 const int maxn = 1e6 + 7;
     43 const int maxm = 8e6 + 10;
     44 const int INF = 0x3f3f3f3f;
     45 const int mod = 10007;
     46 
     47 //rnk从0开始
     48 //sa从1开始,因为最后一个字符(最小的)排在第0位
     49 //height从1开始,因为表示的是sa[i - 1]和sa[i]
     50 //倍增算法 O(nlogn)
     51 int wa[maxn], wb[maxn], wv[maxn], ws_[maxn];
     52 int Rank[maxn], height[maxn], sa[maxn], r[maxn];
     53 int n, maxx;
     54 char s[maxn];
     55 //Suffix函数的参数m代表字符串中字符的取值范围,是基数排序的一个参数,如果原序列都是字母可以直接取128,如果原序列本身都是整数的话,则m可以取比最大的整数大1的值
     56 //待排序的字符串放在r数组中,从r[0]到r[n-1],长度为n
     57 //为了方便比较大小,可以在字符串后面添加一个字符,这个字符没有在前面的字符中出现过,而且比前面的字符都要小
     58 //同上,为了函数操作的方便,约定除r[n-1]外所有的r[i]都大于0,r[n-1]=0
     59 //函数结束后,结果放在sa数组中,从sa[0]到sa[n-1]
     60 void Suffix ( int *r, int *sa, int n, int m ) {
     61     int i, j, k, *x = wa, *y = wb, *t;
     62     //对长度为1的字符串排序
     63     //一般来说,在字符串的题目中,r的最大值不会很大,所以这里使用了基数排序
     64     //如果r的最大值很大,那么把这段代码改成快速排序
     65     for ( i = 0; i < m; ++i ) ws_[i] = 0;
     66     for ( i = 0; i < n; ++i ) ws_[x[i] = r[i]]++; //统计字符的个数
     67     for ( i = 1; i < m; ++i ) ws_[i] += ws_[i - 1]; //统计不大于字符i的字符个数
     68     for ( i = n - 1; i >= 0; --i ) sa[--ws_[x[i]]] = i; //计算字符排名
     69     //基数排序
     70     //x数组保存的值相当于是rank值
     71     for ( j = 1, k = 1; k < n; j *= 2, m = k ) {
     72         //j是当前字符串的长度,数组y保存的是对第二关键字排序的结果
     73         //第二关键字排序
     74         for ( k = 0, i = n - j; i < n; ++i ) y[k++] = i; //第二关键字为0的排在前面
     75         for ( i = 0; i < n; ++i ) if ( sa[i] >= j ) y[k++] = sa[i] - j; //长度为j的子串sa[i]应该是长度为2 * j的子串sa[i] - j的后缀(第二关键字),对所有的长度为2 * j的子串根据第二关键字来排序
     76         for ( i = 0; i < n; ++i ) wv[i] = x[y[i]]; //提取第一关键字
     77         //按第一关键字排序 (原理同对长度为1的字符串排序)
     78         for ( i = 0; i < m; ++i ) ws_[i] = 0;
     79         for ( i = 0; i < n; ++i ) ws_[wv[i]]++;
     80         for ( i = 1; i < m; ++i ) ws_[i] += ws_[i - 1];
     81         for ( i = n - 1; i >= 0; --i ) sa[--ws_[wv[i]]] = y[i]; //按第一关键字,计算出了长度为2 * j的子串排名情况
     82         //此时数组x是长度为j的子串的排名情况,数组y仍是根据第二关键字排序后的结果
     83         //计算长度为2 * j的子串的排名情况,保存到数组x
     84         t = x;
     85         x = y;
     86         y = t;
     87         for ( x[sa[0]] = 0, i = k = 1; i < n; ++i )
     88             x[sa[i]] = ( y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j] ) ? k - 1 : k++;
     89         //若长度为2 * j的子串sa[i]与sa[i - 1]完全相同,则他们有相同的排名
     90     }
     91 }
     92 void calheight ( int *r, int *sa, int n ) {
     93     int i, j, k = 0;
     94     for ( i = 1; i <= n; i++ ) Rank[sa[i]] = i;
     95     for ( i = 0; i < n; height[Rank[i++]] = k )
     96         for ( k ? k-- : 0, j = sa[Rank[i] - 1]; r[i + k] == r[j + k]; k++ );
     97 }
     98 
     99 int num, id[maxn], vis[4005], idx;
    100 int check ( int mid ) {
    101     int cnt = 0;
    102     mem ( vis, 0 );
    103     for ( int i = 2 ; i <= n ; i++ ) {
    104         if ( height[i] < mid ) {
    105             if ( cnt >= num ) {
    106                 idx = sa[i - 1];
    107                 return 1;
    108             }
    109             mem ( vis, 0 );
    110             cnt = 0;
    111         } else {
    112             if ( !vis[id[sa[i]]] ) vis[id[sa[i]]] = 1, cnt++;
    113             if ( !vis[id[sa[i - 1]]] ) vis[id[sa[i - 1]]] = 1, cnt++;
    114         }
    115     }
    116     if ( cnt >= num ) {
    117         idx = s[n];
    118         return 1;
    119     }
    120     return 0;
    121 }
    122 int T;
    123 int main() {
    124     sf ( T );
    125     while ( T-- ) {
    126         sf ( num );
    127         n = 0;
    128         int low = 1, high = INF;
    129         for ( int i = 1 ; i <= num ; i++ ) {
    130             scanf ( "%s", s );
    131             int len = strlen ( s );
    132             high = min ( high, len );
    133             for ( int j = 0 ; j < len ; j++ ) {
    134                 r[n + j] = s[j] + 4001;
    135                 maxx = max ( maxx, r[n + j] );
    136                 id[n + j] = i;
    137             }
    138             r[n + len] = i;
    139             id[n + len] = 0;
    140             n += len + 1;
    141         }
    142         r[n] = 0;
    143 //        for ( int i = 0; i <= n ; i++ ) printf ( "%d%c", r[i], ( i == n ? '
    ' : ' ' ) );
    144 //        for ( int i = 0; i <= n ; i++ ) printf ( "%d%c", id[i], ( i == n ? '
    ' : ' ' ) );
    145         Suffix ( r, sa, n + 1, maxx + 1 );
    146         calheight ( r, sa, n );
    147 
    148         int maxlen = 0;
    149         while ( low <= high ) {
    150             int mid = ( low + high ) / 2;
    151             if ( check ( mid ) ) maxlen = mid, low = mid + 1;
    152             else high = mid - 1;
    153         }
    154         check ( maxlen );
    155 //        fuck(maxlen);
    156         if ( maxlen >=3)  {
    157             for ( int i = idx ; i < idx + maxlen ; i++ ) printf ( "%c", r[i] - 4001 );
    158 //            printf ( "%d
    ", maxlen );
    159             printf ( "
    " );
    160         } else printf ( "no significant commonalities
    " );
    161     }
    162     return 0;
    163 }
    View Code
  • 相关阅读:
    Spring MVC 完全注解方式配置web项目
    spring WebServiceTemplate 调用 axis1.4 发布的webservice
    修改Intellij Idea 创建maven项目默认Java编译版本
    Git Commit提交规范和IDEA插件Git Commit Template的使用
    myEclipse10安装以及破解
    ES6中Map与其他数据结构的互相转换
    ES6用来判断数值的相关函数
    WebStorm使用码云插件问题
    Css解决表格超出部分用省略号显示
    Js/Jquery获取网页屏幕可见区域高度
  • 原文地址:https://www.cnblogs.com/qldabiaoge/p/11330195.html
Copyright © 2020-2023  润新知