• 2016 China-Final-F题 ——(SA+二分)


      其实是一个很经典的字符串问题,但是我们比赛的时候没出。

      先看一下UVA11107这题,题意是,找出最长的一个字符串,在至少一半的字符串中出现过。只要把所有的字符串用不同的分隔符分开,然后SA一下,最后二分长度,用height将字符串分组,判断是否超过一半即可。要注意的是,因为分隔符单单用个char已经不够了,所以全部char都换成int,然后用不同的整数来作为分隔符即可。

      代码如下:

      1 #include <stdio.h>
      2 #include <algorithm>
      3 #include <string.h>
      4 #include <vector>
      5 using namespace std;
      6 const int N = 100000 + 1000;
      7 typedef long long ll;
      8 const int sep = 'z' + 1;
      9 
     10 /**
     11  *    sa[i]:表示排在第i位的后缀的起始下标
     12  *    rank[i]:表示后缀suffix(i)排在第几
     13  *    height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值
     14  *
     15  * */
     16  /*
     17     如果整数的话模板改成int.
     18     加一个数a[n] = 0 。 这样他的排名是第一个。
     19     construct(a,n+1);
     20 
     21     字符串的话。
     22     len = strlen(str);
     23     construct(s,strlen(s)+1);
     24     排名第0的是个空字符串。
     25 
     26     height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值
     27     所以height[1] = 0;
     28     rank[len] = 0;
     29     sa[0] = len;
     30  */
     31 int sa[N],rnk[N],height[N];
     32 void construct(const int *s,int n,int m = 256) {
     33     static int t1[N],t2[N],c[N];
     34     int *x = t1,*y = t2;
     35     int i,j,k,p,l;
     36     for (i = 0; i < m; ++ i) c[i] = 0;
     37     for (i = 0; i < n; ++ i) c[x[i] = s[i]] ++;
     38     for (i = 1; i < m; ++ i) c[i] += c[i - 1];
     39     for (i = n - 1; i >= 0; -- i) sa[--c[x[i]]] = i;
     40     for (k = 1; k <= n; k <<= 1) {
     41         p = 0;
     42         for (i = n - k; i < n; ++ i) y[p++] = i;
     43         for (i = 0; i < n; ++ i) if (sa[i] >= k) y[p++] = sa[i] - k;
     44         for (i = 0; i < m; ++ i) c[i] = 0;
     45         for (i = 0; i < n; ++ i) c[x[y[i]]] ++;
     46         for (i = 1; i < m; ++ i) c[i] += c[i - 1];
     47         for (i = n - 1; i >= 0; -- i) sa[--c[x[y[i]]]] = y[i];
     48         std::swap(x,y);
     49         p = 1; x[sa[0]] = 0;
     50         for (i = 1; i < n; ++ i)
     51             x[sa[i]] = y[sa[i - 1]] == y[sa[i]]
     52                 && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1: p ++;
     53         if (p >= n) break;
     54         m = p;
     55     }
     56     for (i = 0; i < n; ++ i) rnk[sa[i]] = i;
     57     for (i = 0,l = 0; i < n; ++ i) {
     58         if (rnk[i]) {
     59             j = sa[rnk[i] - 1];
     60             while (s[i + l] == s[j + l]) l++;
     61             height[rnk[i]] = l;
     62             if (l) l--;
     63         }
     64     }
     65 }
     66 
     67 char str[1005];
     68 int s[N];
     69 int End[105],len,n;
     70 bool vis[105];
     71 vector<int> ans;
     72 
     73 bool solve(int Len)
     74 {
     75     ans.clear();
     76     int cnt = 0;
     77     memset(vis,false,sizeof(vis));
     78     for(int i=2;i<=len;i++)
     79     {
     80         if(height[i] >= Len)
     81         {
     82             for(int j=1;j<=n;j++)
     83             {
     84                 if(sa[i] > End[j-1] && sa[i] < End[j]) {cnt += !vis[j]; vis[j] = 1;}
     85                 if(sa[i-1] > End[j-1] && sa[i-1] < End[j]) {cnt += !vis[j]; vis[j] = 1;}
     86             }
     87         }
     88         else
     89         {
     90             if(cnt > n / 2) ans.push_back(sa[i-1]);
     91             cnt = 0;
     92             memset(vis,false,sizeof(vis));
     93         }
     94     }
     95     if(cnt > n / 2) ans.push_back(sa[len]);
     96     return ans.size();
     97 }
     98 
     99 int main()
    100 {
    101     int first = 0;
    102     while(scanf("%d",&n) == 1 && n)
    103     {
    104         if(first == 0) first = 1;
    105         else puts("");
    106         len = 0;
    107         for(int i=1;i<=n;i++)
    108         {
    109             scanf("%s",str);
    110             for(int j=0;str[j];j++) s[len++] = str[j];
    111 
    112             s[len++] = sep + i;
    113             End[i] = len-1;
    114         }
    115         s[len] = 0;
    116         construct(s,len+1);
    117         int l = 1, r = len, Ans = -1;
    118         while(l <= r)
    119         {
    120             int mid = l + r >> 1;
    121             if(solve(mid)) l = mid + 1, Ans = mid;
    122             else r = mid - 1;
    123         }
    124 
    125         if(Ans == -1) puts("?");
    126         else
    127         {
    128             // 为了得到答案,再solve一遍
    129             solve(Ans);
    130             for(int i=0;i<ans.size();i++)
    131             {
    132                 for(int j=ans[i],cnt=1;cnt<=Ans;j++,cnt++) putchar(s[j]);
    133                 puts("");
    134             }
    135             //puts("");
    136         }
    137     }
    138     return 0;
    139 }
    View Code

      然后看下F题,题意是,找出最短的一个字符串,只在第一个字符串中出现。那么,一样的套路:全部连接,然后SA一遍,然后分组,找这一组中是不是都只是在第一个字符串中出现(这里采用了一个belong数组来判断每个字符是属于哪个串中的)。

      代码如下:

      1 #include <stdio.h>
      2 #include <algorithm>
      3 #include <string.h>
      4 #include <vector>
      5 using namespace std;
      6 const int N = 3e6+7;
      7 const int M = 5e4+7;
      8 typedef long long ll;
      9 const int sep = 'z' + 1;
     10 
     11 /**
     12  *    sa[i]:表示排在第i位的后缀的起始下标
     13  *    rank[i]:表示后缀suffix(i)排在第几
     14  *    height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值
     15  *
     16  * */
     17  /*
     18     如果整数的话模板改成int.
     19     加一个数a[n] = 0 。 这样他的排名是第一个。
     20     construct(a,n+1);
     21 
     22     字符串的话。
     23     len = strlen(str);
     24     construct(s,strlen(s)+1);
     25     排名第0的是个空字符串。
     26 
     27     height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值
     28     所以height[1] = 0;
     29     rank[len] = 0;
     30     sa[0] = len;
     31  */
     32 int sa[N],rnk[N],height[N];
     33 void construct(const int *s,int n,int m = 256) {
     34     static int t1[N],t2[N],c[N];
     35     int *x = t1,*y = t2;
     36     int i,j,k,p,l;
     37     for (i = 0; i < m; ++ i) c[i] = 0;
     38     for (i = 0; i < n; ++ i) c[x[i] = s[i]] ++;
     39     for (i = 1; i < m; ++ i) c[i] += c[i - 1];
     40     for (i = n - 1; i >= 0; -- i) sa[--c[x[i]]] = i;
     41     for (k = 1; k <= n; k <<= 1) {
     42         p = 0;
     43         for (i = n - k; i < n; ++ i) y[p++] = i;
     44         for (i = 0; i < n; ++ i) if (sa[i] >= k) y[p++] = sa[i] - k;
     45         for (i = 0; i < m; ++ i) c[i] = 0;
     46         for (i = 0; i < n; ++ i) c[x[y[i]]] ++;
     47         for (i = 1; i < m; ++ i) c[i] += c[i - 1];
     48         for (i = n - 1; i >= 0; -- i) sa[--c[x[y[i]]]] = y[i];
     49         std::swap(x,y);
     50         p = 1; x[sa[0]] = 0;
     51         for (i = 1; i < n; ++ i)
     52             x[sa[i]] = y[sa[i - 1]] == y[sa[i]]
     53                 && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1: p ++;
     54         if (p >= n) break;
     55         m = p;
     56     }
     57     for (i = 0; i < n; ++ i) rnk[sa[i]] = i;
     58     for (i = 0,l = 0; i < n; ++ i) {
     59         if (rnk[i]) {
     60             j = sa[rnk[i] - 1];
     61             while (s[i + l] == s[j + l]) l++;
     62             height[rnk[i]] = l;
     63             if (l) l--;
     64         }
     65     }
     66 }
     67 
     68 char str[N];
     69 int s[N];
     70 int endlen[N],len,n,belong[N];
     71 char temp[N];
     72 int all;
     73 int from;
     74 
     75 bool check(int st,int ed,int L)
     76 {
     77     int now = -1;
     78     for(int i=st;i<=ed;i++)
     79     {
     80         if(belong[sa[i]] == 1 && endlen[sa[i]] >= L)
     81         {
     82             if(now == -1) now = sa[i];
     83             continue;
     84         }
     85         return false;
     86     }
     87     from = now;
     88     return true;
     89 }
     90 
     91 bool solve(int L)
     92 {
     93     int st = 1, ed = 1;
     94     for(int i=2;i<all;i++)
     95     {
     96         if(height[i] >= L) ed++;
     97         else
     98         {
     99             if(check(st,ed,L)) return 1;
    100             st = ed = i;
    101         }
    102     }
    103     return check(st,ed,L);
    104 }
    105 
    106 int main()
    107 {
    108     int T, kase = 1;
    109     scanf("%d",&T);
    110     while(T--)
    111     {
    112         printf("Case #%d: ",kase++);
    113         scanf("%d%s",&n,temp);
    114         int now_len = strlen(temp);
    115         len = 0;
    116         for(int i=0;temp[i];i++)
    117         {
    118             s[len] = temp[i];
    119             belong[len] = 1;
    120             endlen[len] = now_len - i;
    121             len ++;
    122         }
    123         s[len] = sep + 1; belong[len] = 1, endlen[len] = 0;
    124         int first_len = len ++;
    125         for(int i=2;i<=n;i++)
    126         {
    127             scanf("%s",str);
    128             int now_len = strlen(str);
    129             for(int j=0;j<now_len;j++)
    130             {
    131                 s[len] = str[j];
    132                 belong[len] = i;
    133                 endlen[len] = now_len - j;
    134                 len ++;
    135             }
    136             s[len] = sep + i; belong[len] = i, endlen[len] = 0; len++;
    137         }
    138         s[len] = 0;
    139         construct(s,len+1,50000 + 500);
    140         all = rnk[first_len];
    141         int l = 0, r = first_len, ans = -1;
    142         while(l <= r)
    143         {
    144             int mid = l + r >> 1;
    145             if(solve(mid)) r = mid - 1, ans = mid;
    146             else l = mid + 1;
    147         }
    148         if(ans == -1) puts("Impossible");
    149         else
    150         {
    151             for(int i=from;i<from+ans;i++) printf("%c",temp[i]);
    152             puts("");
    153         }
    154     }
    155     return 0;
    156 }
    View Code

      感觉二分+SA就是一种套路,可以结合前几天做的那题一起看看,最少出现m次的最长字符串。

  • 相关阅读:
    使用hugo在gitee上写blog
    golang初识2
    golang初识1
    install go on ubuntu
    sql优化的几种方式
    UpdatePanel 无刷新弹出窗口
    .net web 点击链接在页面指定位置显示DIV的问题
    重建主键
    sql 日期时间格式转换
    UpdatePanel无法直接弹出窗口的解决
  • 原文地址:https://www.cnblogs.com/zzyDS/p/6207169.html
Copyright © 2020-2023  润新知