UVa 11107 生命的形式(不小于k个字符串中的最长子串)

Posted 谦谦君子,陌上其华

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UVa 11107 生命的形式(不小于k个字符串中的最长子串)相关的知识,希望对你有一定的参考价值。

https://vjudge.net/problem/UVA-11107

题意:
给定n个字符串,求出现在不小于n的一半个字符串的最长子串,如果有多个,则按字典序输出。

 

思路:

首先就是将这n个字符串连接起来,然后二分答案,每次只需要判断是否有一个长度为p的串在超过一半的串中连续出现,判断方法是扫描一遍height数组,把它分成若干段,每当height[i]小于p时开辟一个新段,则每一段的最初p个字符均相同。只要某一段中包含了超过n/2个原串的后缀,p就是满足条件的。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<vector>
  6 #include<stack>
  7 #include<queue>
  8 #include<cmath>
  9 #include<map>
 10 #include<set>
 11 using namespace std;
 12 typedef long long ll;
 13 typedef pair<int,int> pll;
 14 const int INF = 0x3f3f3f3f;
 15 const int maxn = 100000+1000;
 16 
 17 int n,k;
 18 int s[maxn];
 19 bool vis[120];
 20 int start[maxn];
 21 int belong[maxn];
 22 int sa[maxn],t[maxn],t2[maxn],c[maxn];
 23 int Rank[maxn],height[maxn];
 24 
 25 
 26 void build_sa(int m)
 27 {
 28     int *x=t,*y=t2;
 29     //基数排序
 30     for(int i=0;i<m;i++)    c[i]=0;
 31     for(int i=0;i<n;i++)    c[x[i]=s[i]]++;
 32     for(int i=1;i<m;i++)    c[i]+=c[i-1];
 33     for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
 34     for(int k=1;k<=n;k<<=1)
 35     {
 36         int p=0;
 37         //直接利用sa数组排序第二关键字
 38         for(int i=n-k;i<n;i++)  y[p++]=i;
 39         for(int i=0;i<n;i++)    if(sa[i]>=k)    y[p++]=sa[i]-k;
 40         //基数排序第一关键字
 41         for(int i=0;i<m;i++)    c[i]=0;
 42         for(int i=0;i<n;i++)    c[x[y[i]]]++;
 43         for(int i=1;i<m;i++)    c[i]+=c[i-1];
 44         for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
 45         //根据sa和y计算新的x数组
 46         swap(x,y);
 47         p=1;
 48         x[sa[0]]=0;
 49         for(int i=1;i<n;i++)
 50             x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
 51         if(p>=n)
 52             break;
 53         m=p;                //下次基数排序的最大值
 54     }
 55 }
 56 
 57 void getHeight(int n)
 58 {
 59     int i,j,k=0;
 60     for(i=1;i<=n;i++)  Rank[sa[i]]=i;
 61     for(i=0;i<n;i++)
 62     {
 63         if(k)  k--;
 64         int j=sa[Rank[i]-1];
 65         while(s[i+k]==s[j+k])  k++;
 66         height[Rank[i]]=k;
 67     }
 68 }
 69 
 70 
 71 bool judge(int n, int len, int num)
 72 {
 73     int size=0;
 74     int cnt = 0;
 75     memset(vis,0,sizeof(vis));
 76     cnt++;
 77     vis[belong[sa[0]]] = 1;
 78     for(int i = 1;i < n;i++)
 79     {
 80         if(height[i] < len)
 81         {
 82             if(cnt>=num)  start[++size]=sa[i-1];  //可行,保存好起点
 83             memset(vis,0,sizeof(vis));
 84             vis[belong[sa[i]]] = 1;
 85             cnt=1;
 86         }
 87         else
 88             if(!vis[belong[sa[i]]])
 89             {
 90                 cnt++;
 91                 vis[belong[sa[i]]] = 1;
 92             }
 93     }
 94     if(cnt>=num)  start[++size]=sa[n-1];  //这儿需要注意,不要忽略了最后一段
 95     if(size)
 96     {
 97         start[0]=size;
 98         return 1;
 99     }
100     return 0;
101 }
102 
103 char str[1005];
104 int main()
105 {
106     //freopen("in.txt","r",stdin);
107     bool flag=true;
108     while(~scanf("%d",&k) && k)
109     {
110         if(!flag) printf("\n");
111         else flag = false;
112         int pos=0,cas=1;
113         int l=0,r=0;
114         for(int i=1;i<=k;i++)
115         {
116             scanf("%s",str);
117             int len=strlen(str);
118             r=max(r,len);
119             for(int j=0;j<len;j++)
120             {
121                 s[pos+j]=(int)str[j]+5;
122                 belong[pos+j] = i;
123             }
124             s[pos+len]=cas++;
125             pos=pos+len+1;
126         }
127         s[pos]=0;
128         n=pos;
129         build_sa(150);
130         getHeight(n-1);
131         int ans=0;
132         while(l <= r)
133         {
134             int mid = (l+r) >> 1;
135             if(judge(pos,mid,k/2+1))
136             {
137                 ans = mid;
138                 l = mid + 1;
139             }
140             else r = mid - 1;
141         }
142         if(ans == 0) printf("?\n");
143         else
144         {
145             for(int i=1;i<=start[0];i++)
146             {
147                 for(int j=start[i];j<start[i]+ans;j++)
148                     printf("%c",s[j]-5);
149                 printf("\n");
150             }
151         }
152     }
153     return 0;
154 }

 

以上是关于UVa 11107 生命的形式(不小于k个字符串中的最长子串)的主要内容,如果未能解决你的问题,请参考以下文章

POJ 3294 UVA 11107 Life Forms 后缀数组

UVA-11107 Life Forms(后缀数组)

UVA11107 训练指南Life Forms后缀数组

UVA 1610 Party Games

UVA 1213 Sum of Different Primes(经典dp)

UVA - 455(周期串)