• 【后缀数组之应用】【待续】


    【最长重复子串问题】

    可重叠最长重复子串 -- POJ 3261

    Disc: 给出包含n个元素的数组a,问其中最长可重叠重复子串的长度,要求该子串至少重复k次;输入保证至少存在一个重复k次的最长子串;

    Tips: 二分查找子串的长度,注意对该长度的子串是否存在K个重复子串的判定方法;

    代码:

      1 /*
      2 Problem: POJ 3261
      3 Tips: 可重复最长重复子串,后缀数组,二分
      4 Time: 172ms
      5 Date: 2015.8.31
      6 */
      7 #include <iostream>
      8 #include <cstring>
      9 #include <cstddef>
     10 #include <cstdio>
     11 #include <string>
     12 #include <algorithm>
     13 const int maxn = 100005;
     14 const int maxm = 1000005;
     15 int wa[maxm],wb[maxn],wv[maxn],ws[maxm];
     16 int sa[maxn], rank[maxn],height[maxn];
     17 int cmp(int *rank, int a,int b,int l)
     18 {
     19     return rank[a]==rank[b] && rank[a+l]==rank[b+l];
     20 }
     21 void debug1(int* r, int n)
     22 {
     23     for(int i = 0; i < n; i++)
     24     {
     25         printf("sa[%d]: %d
    ", i, sa[i]);
     26         int p = sa[i];
     27         for(; p < n; p++)
     28             printf("%d ", r[p]);
     29         printf("
    ");
     30     }
     31 }
     32 void debug2(int* r, int n)
     33 {
     34     for(int i = 0; i < n; i++)
     35     {
     36         printf("height[rank[%d]]: %d
    ", i, height[rank[i]]);
     37     }
     38 }
     39 void da(int *r,int n,int m)
     40 {
     41     int i, k, p, *x=wa, *y=wb, *t;
     42     for(i=0;i<m;i++) ws[i] = 0;
     43     for(i=0;i<n;i++) ws[x[i] = r[i]]++;
     44     for(i=1;i<m;i++) ws[i] += ws[i-1];
     45     for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
     46     for(k=1, p=1; p<n; k*=2, m=p)
     47     {
     48         for(p=0,i=n-k;i<n;i++) y[p++]=i;
     49         for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
     50         for(i=0;i<n;i++) wv[i]=x[y[i]];
     51         for(i=0;i<m;i++) ws[i]=0;
     52         for(i=0;i<n;i++) ws[wv[i]]++;
     53         for(i=1;i<m;i++) ws[i]+=ws[i-1];
     54         for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
     55         t=x,x=y,y=t;
     56         for(p=1,x[sa[0]]=0,i=1;i<n;i++)
     57             x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++;
     58     }
     59     //debug1(r, n);
     60     return;
     61 }
     62 void calheight(int *r,int n)
     63 {
     64     int i,j,k=0;
     65     for(i=1;i<=n;i++) rank[sa[i]]=i;
     66     for(i=0;i<n;height[rank[i++]]=k)
     67         for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++)
     68             ;
     69     //debug2(r, n);
     70     return;
     71 }
     72 bool check(int mid, int n, int k) //Notice!
     73 {
     74     int i = 1; //height[0] = 0;
     75     while(1)
     76     {
     77         while(i <= n && height[i] < mid)    i++;
     78         if(i == n+1) break;
     79         int cnt = 1;
     80         while(i <= n && height[i] >= mid)   i++, cnt++;
     81         if(cnt >= k) return true;
     82     }
     83     return false;
     84 }
     85 int bin_search(int n, int k)
     86 {
     87     int l = 1, r = n, mid;
     88     int ans = 0;
     89     while(l <= r)
     90     {
     91         mid = l+(r-l)/2;
     92         if(check(mid, n, k))
     93             ans = mid, l = mid+1;
     94         else
     95             r = mid-1;
     96     }
     97     return ans;
     98 }
     99 int main()
    100 {
    101     int n, k, r[maxn];
    102     scanf("%d%d", &n, &k);
    103     for(int i = 0; i < n; i++) {scanf("%d", &r[i]); r[i]++;}
    104     r[n] = 0;
    105     da(r,n+1,maxm);
    106     calheight(r,n);
    107     int ans = bin_search(n, k);
    108     printf("%d
    ", ans);
    109     return 0;
    110 }
    View Code

    不可重叠最长重复子串 -- POJ 1743

    Disc: 给出一段长度为n的数组,其元素大小在1-88之间,现在要求出这个串中最长的不重复的两个相同的子串,满足要求的子串的定义是长度不短于5,其两个子串可以通过分别加上某一值或减去某一值得到,两子串不能够重叠。

    Tips: 转化为相邻两元素差数组,求其不可重叠最长重复子串,且长度不可小于4;

         二分查找子串的长度,将height[]数组分组,判断连续的height[]大于等于k的一段中sa[i] -sa[j] 是否 >= k (height[i , j+1] >= k),若存在此情况,则证明该长度下存在不重叠重复子串;

    代码:

     1 #include <iostream>
     2 #include <cstring>
     3 #include <cstdio>
     4 #include <cstdio>
     5 #include <string>
     6 #include <algorithm>
     7 const int maxn = 100001;
     8 int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
     9 int rank[maxn],height[maxn],sa[maxn];
    10 int cmp(int *rank, int a,int b,int l)
    11 {
    12     return rank[a]==rank[b] && rank[a+l]==rank[b+l];
    13 }
    14 void da(int *r,int n,int m)
    15 {
    16     int i, k, p, *x=wa, *y=wb, *t;
    17 
    18     for(i=0;i<m;i++) ws[i] = 0;
    19     for(i=0;i<n;i++) ws[x[i] = r[i]]++;
    20     for(i=1;i<m;i++) ws[i] += ws[i-1];
    21     for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
    22     for(k=1, p=1; p<n; k*=2, m=p)
    23     {
    24         p=0; for(i=n-k;i<n;i++) y[p++]=i;
    25         for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
    26         for(i=0;i<n;i++) wv[i]=x[y[i]];
    27         for(i=0;i<m;i++) ws[i]=0;
    28         for(i=0;i<n;i++) ws[wv[i]]++;
    29         for(i=1;i<m;i++) ws[i]+=ws[i-1];
    30         for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
    31         t=x,x=y,y=t;
    32         for(p=1,x[sa[0]]=0,i=1;i<n;i++)
    33             x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++;
    34     }
    35     return;
    36 }
    37 
    38 void calheight(int *r,int n)
    39 {
    40     int i,j,k=0;
    41     for(i=1;i<=n;i++) rank[sa[i]]=i;
    42     for(i=0;i<n;height[rank[i++]]=k)
    43         for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++)
    44             ;
    45     return;
    46 }
    47 bool check(int k, int n)
    48 {
    49     for(int i = 1; i <= n; i++)
    50     {
    51         if(height[i] < k) continue;
    52         for(int j = i-1; j >= 1; j--)
    53         {
    54             if(abs(sa[i]-sa[j]) >= k) return true;
    55             if(height[j] < k) break;
    56         }
    57     }
    58     return false;
    59 }
    60 int bin_search(int n)
    61 {
    62     int l = 1, r = n, mid, ans = 0;
    63     while(l <= r)
    64     {
    65         mid = l+(r-l)/2;
    66         if(check(mid, n))
    67             ans = mid, l = mid+1;
    68         else
    69             r = mid-1;
    70     }
    71     return ans;
    72 }
    73 
    74 int main()
    75 {
    76     int n, r[maxn];
    77     while(~scanf("%d", &n) && n)
    78     {
    79         n--;
    80         int a, b;   scanf("%d", &a);
    81         for(int i = 0; i < n; i++)
    82         {
    83             scanf("%d", &b);
    84             r[i] = b-a+100;
    85             a = b;
    86         }
    87         r[n] = 0;
    88 
    89         if(n<10) {printf("0
    "); continue;}
    90 
    91         da(r,n+1,200);
    92         calheight(r,n);
    93 
    94         int ans = bin_search(n);
    95         printf("%d
    ", ans>=4 ? ++ans : 0);
    96     }
    97     return 0;
    98 }
    View Code

    【回文子串问题】

    最长回文子串 -- URAL 1297

    Disc: 给出字符串,输出其最长回文子串

    Tips: 对于字符串上的每个位置i,如果知道从i开始的后缀和到i为止的前缀反转后的字符串的最长公共前缀的话,就知道了以第i个字符为对称中心的最长回文的长度了;
            因此我们用在S中不会出现的字符('$')将S和S反转后的字符串拼接起来,得到字符串S',再计算S'的后缀数组和height[]数组。

        对height数组进行相应区间查询最小值RMQ,奇数回文串即区间[rank[i], rank[n-i-1]]中height的最小值,偶数回文串即区间[rank[i], rank[n-i]]中height的最小值

    代码:

      1 /*
      2 Problem: URAL 1297
      3 Disc: 给出字符串,求最长回文子串
      4 Tips: Suffix Array, height[], RMQ
      5       对于字符串上的每个位置i,如果知道从i开始的后缀和到i为止的前缀反转后的字符串的最长公共前缀的话,就知道了以第i个字符为对称中心的最长回文的长度了;
      6       因此我们用在S中不会出现的字符('$')将S和S反转后的字符串拼接起来,得到字符串S',再计算S'的后缀数组。
      7 Date: 2015.9.1
      8 */
      9 #include<iostream>
     10 #include<cstdio>
     11 #include<cstring>
     12 using namespace std;
     13 const int maxn = 1002;
     14 int sa[maxn], rank[maxn], height[maxn];
     15 int wa[maxn], wb[maxn], wv[maxn], wd[maxn];
     16 int cmp(int *r, int a, int b, int l){
     17     return r[a] == r[b] && r[a+l] == r[b+l];
     18 }
     19 void da(int *r, int n, int m){          //  倍增算法 r为待匹配数组  n为总长度 m为字符范围
     20     int i, j, p, *x = wa, *y = wb, *t;
     21     for(i = 0; i < m; i ++) wd[i] = 0;
     22     for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++;
     23     for(i = 1; i < m; i ++) wd[i] += wd[i-1];
     24     for(i = n-1; i >= 0; i --) sa[-- wd[x[i]]] = i;
     25     for(j = 1, p = 1; p < n; j *= 2, m = p){
     26         for(p = 0, i = n-j; i < n; i ++) y[p ++] = i;
     27         for(i = 0; i < n; i ++) if(sa[i] >= j) y[p ++] = sa[i] - j;
     28         for(i = 0; i < n; i ++) wv[i] = x[y[i]];
     29         for(i = 0; i < m; i ++) wd[i] = 0;
     30         for(i = 0; i < n; i ++) wd[wv[i]] ++;
     31         for(i = 1; i < m; i ++) wd[i] += wd[i-1];
     32         for(i = n-1; i >= 0; i --) sa[-- wd[wv[i]]] = y[i];
     33         for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i ++){
     34             x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1: p ++;
     35         }
     36     }
     37 }
     38 void calheight(int *r, int n){           //  求height数组。
     39     int i, j, k = 0;
     40     for(i = 1; i <= n; i ++) rank[sa[i]] = i;
     41     for(i = 0; i < n; height[rank[i ++]] = k){
     42         for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++);
     43     }
     44 }
     45 
     46 /*******************非模板区***********************/
     47 int Log2[maxn];
     48 void init()
     49 {
     50     Log2[0] = -1; Log2[1] = 0;
     51     int i, j;
     52     for(i = 2, j = 0; i < maxn; i++)
     53     {
     54         if(i > (1<<(j+1))) j++;
     55         Log2[i] = j;
     56     }
     57 }
     58 int dp[maxn][20];
     59 void initRMQ(int n)
     60 {
     61     for(int i = 1; i <= n; i++) dp[i][0] = height[i];
     62     for(int j = 1; j <= 20; j++)
     63         for(int i = 1; i + (1 << (j-1)) <= n; i++)
     64             dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]); //注意是height[]的最小值
     65 }
     66 int query(int L, int R)
     67 {
     68     if(L > R) swap(L, R);
     69     L++;
     70     if(L == R) return dp[L][0];
     71     int len = R-L+1;
     72     int k = Log2[len]; //此处用的是Log2[]数组保存每个数的log值,当然也可以用下面的式子求得;
     73     //int k = (int)(log(des - src + 1.0) / log(2.0));
     74     return min(dp[L][k], dp[R-(1<<k)+1][k]);
     75 }
     76 void solve(char* s, int n)
     77 {
     78     initRMQ(n);
     79     int slen = strlen(s);
     80     int maxl = 1, l, len, start;
     81     for(int i = 0; i < slen; i++)
     82     {
     83         l = query(rank[i], rank[n-i-1]); //奇数回文串
     84         len = l*2-1;
     85         if(len > maxl)  maxl = len, start = i-l+1;
     86 
     87         l = query(rank[i], rank[n-i]);  //偶数回文串
     88         len = l*2;
     89         if(len > maxl)  maxl = len, start = i-l;
     90     }
     91     if(maxl == 1)   printf("%c
    ", s[0]);
     92     else
     93     {
     94         for(int i = start; i < start + maxl; i++)
     95             printf("%c", s[i]);
     96         printf("
    ");
     97     }
     98 }
     99 int main()
    100 {
    101     init();
    102     char s[maxn];
    103     int r[maxn*2];
    104     while(~scanf("%s", s))
    105     {
    106         int len = strlen(s), n = 0;
    107         for(int i = 0; i < len; i++) r[n++] = s[i];
    108         r[n++] = '$';
    109         for(int i = len-1; i >= 0; i--) r[n++] = s[i];
    110         r[n] = 0;
    111         da(r,n+1,128);
    112         calheight(r,n);
    113         solve(s,n);
    114     }
    115     return 0;
    116 }
    View Code

     【公共子串问题】

    最长公共连续子串 --POJ 2774

    Disc: 给定两个字符串A和B,求最长公共连续子串

    Tips: 首先将两个字符串合并成一个字符串,中间由一个字符串中不可能出现的字符分割。通过二分枚举长度,然后在height分组中查看是否存在在两个不同串的后缀即可。

            也可以证明最长的公共子串所在后缀是相邻的,因此可以直接遍历一遍height数组,判定相邻的两个height数组是否属于不同的两个串。 

    code:

     1 /*
     2 Problem: POJ 2774
     3 Tips: 最长公共连续子串
     4 Date: 2015.8.30
     5 */
     6 #include<iostream>
     7 #include<cstdio>
     8 #include<cstring>
     9 using namespace std;
    10 const int maxn = 200002;
    11 int sa[maxn], rank[maxn], height[maxn];
    12 int wa[maxn], wb[maxn], wv[maxn], wd[maxn];
    13 int cmp(int *r, int a, int b, int l){
    14     return r[a] == r[b] && r[a+l] == r[b+l];
    15 }
    16 void da(int *r, int n, int m){          //  倍增算法 r为待匹配数组  n为总长度 m为字符范围
    17     int i, j, p, *x = wa, *y = wb, *t;
    18     for(i = 0; i < m; i ++) wd[i] = 0;
    19     for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++;
    20     for(i = 1; i < m; i ++) wd[i] += wd[i-1];
    21     for(i = n-1; i >= 0; i --) sa[-- wd[x[i]]] = i;
    22     for(j = 1, p = 1; p < n; j *= 2, m = p){
    23         for(p = 0, i = n-j; i < n; i ++) y[p ++] = i;
    24         for(i = 0; i < n; i ++) if(sa[i] >= j) y[p ++] = sa[i] - j;
    25         for(i = 0; i < n; i ++) wv[i] = x[y[i]];
    26         for(i = 0; i < m; i ++) wd[i] = 0;
    27         for(i = 0; i < n; i ++) wd[wv[i]] ++;
    28         for(i = 1; i < m; i ++) wd[i] += wd[i-1];
    29         for(i = n-1; i >= 0; i --) sa[-- wd[wv[i]]] = y[i];
    30         for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i ++){
    31             x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1: p ++;
    32         }
    33     }
    34 }
    35 void calheight(int *r, int n){           //  求height数组。
    36     int i, j, k = 0;
    37     for(i = 1; i <= n; i ++) rank[sa[i]] = i;
    38     for(i = 0; i < n; height[rank[i ++]] = k){
    39         for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++);
    40     }
    41 }
    42 void solve(int n, int l1, int l2)
    43 {
    44     int maxl = 0;
    45     for(int i = 2; i < n; i++)
    46     {
    47         if(height[i] > maxl)
    48         {
    49             if(sa[i-1] >= 0 && sa[i-1] < l1 && sa[i] > l1)
    50                 maxl = height[i];
    51             if(sa[i] >= 0 && sa[i] < l1 && sa[i-1] > l1)
    52                 maxl = height[i];
    53         }
    54     }
    55     printf("%d
    ", maxl);
    56 }
    57 int main()
    58 {
    59     char s1[maxn], s2[maxn];
    60     int s[maxn], r[maxn];
    61     scanf("%s%s", s1, s2);
    62     int l1 = strlen(s1), l2 = strlen(s2);
    63     int n = 0;
    64     for(int i = 0; i < l1; i++)
    65         s[n++] = (s1[i]-'a'+1);
    66     s[n++] = 28;
    67     for(int i = 0; i < l2; i++)
    68         s[n++] = (s2[i]-'a'+1);
    69     s[n] = 0;
    70     da(s,n+1,30);
    71     calheight(s,n);
    72     solve(n, l1, l2);
    73     return 0;
    74 }
    View Code
  • 相关阅读:
    linux7系统进入单用户模式
    GoAccess日志分析工具
    yum总结
    分布式文件系统---GlusterFS
    【centos7】添加开机启动服务/脚本
    centos 7 服务初始化
    chrony软件
    [USACO4.1]麦香牛块Beef McNuggets 题解报告
    组合数的几种计算方法
    【ZJOI2005】沼泽鳄鱼 题解报告
  • 原文地址:https://www.cnblogs.com/LLGemini/p/4772587.html
Copyright © 2020-2023  润新知