有一个细节不是特别懂,然后的话细节有点多,就是挺难发现的那一种,感谢大佬的博客

1470: 后缀数组4:Life Forms

poj3294

时间限制: 1 Sec  内存限制: 128 MB
提交: 112  解决: 35
[提交] [状态] [讨论版] [命题人:admin]

题目描述

【问题描述】

求n个字符串(长度1000)的最长的一个子串,满足该子串在一半以上(不包括一半)的字符串中出现过,并输出该子串,如果有多个子串满足要求,则按字典序输出所有的子串;(全部都是小写字母)

【输入格式】

输入N(1<=N<=100,还真有为1的数据哟)(每个测试点中数据组数不超过100)

【输出格式】

对于每个测试样例,输出答案。如果有很多,则按字典序输出。如果没有解决方案,至少有一个字母,输出“?”在测试用例之间留下一条空行。

接下来是N个字符串

(有多组数据,N为0时结束)

【样例】
输入:

3

abcdefg

bcdefgh

cdefghi

3

xxx

yyy

zzz

0
输出:

bcdefg

cdefgh

?

 然后的话,我就引用一下罗穗骞大佬和zjw大佬的分析
  • 我们把他合并起来了,和上一题的处理方法很相似,但是的话我们的ASCII只有127,所以我们要用一个a数组来保存,才可以跑get_sa
  • 因为合并了,所以一定要保证他们不是在同一个串里面,其次的话,这道题有一个比较神奇的概念,就是后缀分组,我们把后缀分成好几组,看看哪一组是满足条件的,记录答案,取最大值

(引自罗穗骞)

  • 因为我们要后缀分组,所以的话要开一个bool型的v数组判断他有没有出现过,然后用一个belong数组来记录在哪一串
  • 然后的话,不知道有一个就是说get_he里面如果Rank[i]==1,就要直接continue,我也不知道为什么,不然会被卡住
  • 大概就是这些了,剩下的就

代码的实现

(注释版,就是解释了一下数组的意思)

 #include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
int sa[],Rank[],rsort[];
int cnt[],pos[],height[];
int a[],belong[],v[];/*因为我们中间有空格,所以的话要开到至少101000*/
/*belong数组记录属于哪一串,v数组是用来判断两个子串是否分别属于未出现过的子串*/
bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];}
char s[];
void get_sa(int n,int m)
{
int k=,p=,len;
for(int i=;i<=n;i++) Rank[i]=a[i];
memset(rsort,,sizeof(rsort));
for(int i=;i<=n;i++) rsort[Rank[i]]++;
for(int i=;i<=m;i++) rsort[i]+=rsort[i-];
for(int i=n;i>=;i--) sa[rsort[Rank[i]]--]=i;
for(int k=;k<n;k<<=)
{
len=;
for(int i=n-k+;i<=n;i++) pos[++len]=i;
for(int i=;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k;
for(int i=;i<=n;i++) cnt[i]=Rank[pos[i]];
memset(rsort,,sizeof(rsort));
for(int i=;i<=n;i++) rsort[cnt[i]]++;
for(int i=;i<=m;i++) rsort[i]+=rsort[i-];
for(int i=n;i>=;i--) sa[rsort[cnt[i]]--]=pos[i];
for(int i=;i<=n;i++) cnt[i]=Rank[i];
p=; Rank[sa[]]=;
for(int i=;i<=n;i++)
{
if(!cmp(sa[i],sa[i-],k)) p++;
Rank[sa[i]]=p;
}
if(p==n) break; m=p;
}
a[]=; sa[]=;
}
void get_he(int n)
{
int j,k=;
for(int i=;i<=n;i++)
{
if(Rank[i]==) continue;/*一定要这样,但是我也不知道为什么没有这个会错*/
j=sa[Rank[i]-];
if(k) k--;
while(a[j+k]==a[i+k]) k++;
height[Rank[i]]=k;
}
}
bool check(int mid,int k,int n)/*二分搜索答案*/
{
int ans=;
memset(v,,sizeof(v)); v[]=;
if(!v[belong[sa[]]]) ans++;/*没出现过*/
v[belong[sa[]]]=;
for(int i=;i<=n;i++)
{
/*分开两边来判断,只要满足条件就是好的,其实就是分组后缀,看一下哪一组满足条件*/
if(height[i]<mid)/*在左边*/
{
memset(v,,sizeof(v)); v[]=;
ans=;/*初始化一下*/
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;/*判断*/
}
else/*在右边*/
{
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;/*判断*/
}
if(ans>=k)return true;/*满足条件*/
}
return false;
}
void putt(int x,int k,int n)/*输出答案*/
{
int ans=;
memset(v,,sizeof(v)); v[]=;
if(!v[belong[sa[]]]) ans++;/*没出现过*/
v[belong[sa[]]]=;
for(int i=;i<=n;i++)
{
if(height[i]<x)/*在左边*/
{
if(ans>=k)
{
for(int j=sa[i-];j<=sa[i-]+x-;j++) printf("%c",a[j]+'a');
printf("\n");
}/*已经满足条件了就可以直接输出*/
memset(v,,sizeof(v)); v[]=;
ans=;
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;/*判断*/
}
else/*在右边*/
{
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;
}
}
if(ans>=k)/*符合条件就输出*/
{
for(int i=sa[n];i<=sa[n]+x-;i++) printf("%c",a[i]+'a');
printf("\n");
}
}
int main()
{
int t;
while(scanf("%d",&t)!=EOF && t)
{
int n=,tp=,tt=t/+;
for(int i=;i<=t;i++)
{
scanf("%s",s+);
for(int j=;j<=strlen(s+);j++)
{
a[++n]=s[j]-'a';
belong[n]=i;/*属于哪一串*/
}
a[++n]=tp; tp++;/*把二十六个字母保存到A数组里面,ascii只有127位,所以不可以直接保存*/
}
if(t==)/*只有一个的话直接输出就好了*/
{
for(int i=;i<n;i++) printf("%c",a[i]+'a');
printf("\n\n"); continue;
}
get_sa(n,); get_he(n);
int l=,r=n,ans=;
while(l<=r)/*二分找答案*/
{
int mid=(l+r)/;
if(check(mid,tt,n))
{
ans=mid;
l=mid+;
}
else r=mid-;
}
if(!ans) {printf("?\n\n"); continue;}
putt(ans,tt,n); printf("\n");
/*ans是我们最后记录的成立的那个边界范围,就是左右的边界范围*/
}
return ;
}

Tristan Code 注释版

(非注释版,挺好理解的,思路清楚了就是细节的探索咯)

 #include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
int sa[],Rank[],rsort[];
int cnt[],pos[],height[];
int a[],belong[],v[];
bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];}
char s[];
void get_sa(int n,int m)
{
int k=,p=,len;
for(int i=;i<=n;i++) Rank[i]=a[i];
memset(rsort,,sizeof(rsort));
for(int i=;i<=n;i++) rsort[Rank[i]]++;
for(int i=;i<=m;i++) rsort[i]+=rsort[i-];
for(int i=n;i>=;i--) sa[rsort[Rank[i]]--]=i;
for(int k=;k<n;k<<=)
{
len=;
for(int i=n-k+;i<=n;i++) pos[++len]=i;
for(int i=;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k;
for(int i=;i<=n;i++) cnt[i]=Rank[pos[i]];
memset(rsort,,sizeof(rsort));
for(int i=;i<=n;i++) rsort[cnt[i]]++;
for(int i=;i<=m;i++) rsort[i]+=rsort[i-];
for(int i=n;i>=;i--) sa[rsort[cnt[i]]--]=pos[i];
for(int i=;i<=n;i++) cnt[i]=Rank[i];
p=; Rank[sa[]]=;
for(int i=;i<=n;i++)
{
if(!cmp(sa[i],sa[i-],k)) p++;
Rank[sa[i]]=p;
}
if(p==n) break; m=p;
}
a[]=; sa[]=;
}
void get_he(int n)
{
int j,k=;
for(int i=;i<=n;i++)
{
if(Rank[i]==) continue;
j=sa[Rank[i]-];
if(k) k--;
while(a[j+k]==a[i+k]) k++;
height[Rank[i]]=k;
}
}
bool check(int mid,int k,int n)
{
int ans=;
memset(v,,sizeof(v)); v[]=;
if(!v[belong[sa[]]]) ans++;
v[belong[sa[]]]=;
for(int i=;i<=n;i++)
{
if(height[i]<mid)
{
memset(v,,sizeof(v)); v[]=;
ans=;
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;
}
else
{
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;
}
if(ans>=k)return true;
}
return false;
}
void putt(int x,int k,int n)
{
int ans=;
memset(v,,sizeof(v)); v[]=;
if(!v[belong[sa[]]]) ans++;
v[belong[sa[]]]=;
for(int i=;i<=n;i++)
{
if(height[i]<x)
{
if(ans>=k)
{
for(int j=sa[i-];j<=sa[i-]+x-;j++) printf("%c",a[j]+'a');
printf("\n");
}
memset(v,,sizeof(v)); v[]=;
ans=;
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;
}
else
{
if(!v[belong[sa[i]]]) ans++;
v[belong[sa[i]]]=;
}
}
if(ans>=k)
{
for(int i=sa[n];i<=sa[n]+x-;i++) printf("%c",a[i]+'a');
printf("\n");
}
}
int main()
{
int t;
while(scanf("%d",&t)!=EOF && t)
{
int n=,tp=,tt=t/+;
for(int i=;i<=t;i++)
{
scanf("%s",s+);
for(int j=;j<=strlen(s+);j++)
{
a[++n]=s[j]-'a';
belong[n]=i;
}
a[++n]=tp; tp++;
}
if(t==)
{
for(int i=;i<n;i++) printf("%c",a[i]+'a');
printf("\n\n"); continue;
}
get_sa(n,); get_he(n);
int l=,r=n,ans=;
while(l<=r)
{
int mid=(l+r)/;
if(check(mid,tt,n))
{
ans=mid;
l=mid+;
}
else r=mid-;
}
if(!ans) {printf("?\n\n"); continue;}
putt(ans,tt,n); printf("\n");
}
return ;
}

Tristan Code 非注释版

 /*二分搜索的时候直接记录答案*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
int sa[],Rank[],rsort[];
int cnt[],pos[],h[],ans[],anslen;
int a[],belong[],v[];
bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];}
char s[];
void get_sa(int n,int m)
{
int k=,p=,len;
for(int i=;i<=n;i++) Rank[i]=a[i];
memset(rsort,,sizeof(rsort));
for(int i=;i<=n;i++) rsort[Rank[i]]++;
for(int i=;i<=m;i++) rsort[i]+=rsort[i-];
for(int i=n;i>=;i--) sa[rsort[Rank[i]]--]=i;
for(int k=;k<n;k<<=)
{
len=;
for(int i=n-k+;i<=n;i++) pos[++len]=i;
for(int i=;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k;
for(int i=;i<=n;i++) cnt[i]=Rank[pos[i]];
memset(rsort,,sizeof(rsort));
for(int i=;i<=n;i++) rsort[cnt[i]]++;
for(int i=;i<=m;i++) rsort[i]+=rsort[i-];
for(int i=n;i>=;i--) sa[rsort[cnt[i]]--]=pos[i];
for(int i=;i<=n;i++) cnt[i]=Rank[i];
p=; Rank[sa[]]=;
for(int i=;i<=n;i++)
{
if(!cmp(sa[i],sa[i-],k)) p++;
Rank[sa[i]]=p;
}
if(p==n) break; m=p;
}
a[]=; sa[]=;
}
void get_he(int n)
{
int j,k=;
for(int i=;i<=n;i++)
{
if(Rank[i]==) continue;
j=sa[Rank[i]-];
if(k) k--;
while(a[j+k]==a[i+k]) k++;
h[Rank[i]]=k;
}
}
bool check(int mid,int k,int n)
{
int sum=; bool bk=;
memset(v,,sizeof(v)); v[]=;
if(!v[belong[sa[]]]) v[belong[sa[]]]=,sum++;
for(int i=;i<=n;i++)
{
if(h[i]<mid)
{
if(sum>=k)
{
if(!bk) anslen=;
ans[++anslen]=sa[i-];bk=;
}
memset(v,,sizeof(v)); v[]=; sum=;
if(!v[belong[sa[i]]]) v[belong[sa[i]]]=,sum++;
}
else if(!v[belong[sa[i]]]) v[belong[sa[i]]]=,sum++;
}
if(sum>=k)
{
if(!bk) anslen=;
ans[++anslen]=sa[n],bk=;
}
return bk;
}
void write(int x)
{
if(!x){printf("?\n\n"); return ;}
for(int i=;i<=anslen;i++)
{
for(int j=ans[i];j<=ans[i]+x-;j++) printf("%c",a[j]+'a');
printf("\n");
}
printf("\n");
}
int main()
{
int t;
while(scanf("%d",&t)!=EOF && t)
{
int n=,tp=,tt=t/+;
for(int i=;i<=t;i++)
{
scanf("%s",s+);
for(int j=;j<=strlen(s+);j++)
{
a[++n]=s[j]-'a';
belong[n]=i;
}
a[++n]=tp; tp++;
}
if(t==)
{
for(int i=;i<n;i++) printf("%c",a[i]+'a');
printf("\n\n"); continue;
}
get_sa(n,); get_he(n);
int l=,r=n,ans=;
while(l<=r)
{
int mid=(l+r)/;
if(check(mid,tt,n))
{
ans=mid;
l=mid+;
}
else r=mid-;
}
write(ans);
}
return ;
}

常数较小,较快 Code

后缀数组练习4:Life Forms的更多相关文章

  1. 后缀数组 UVA 11107 Life Forms

    题目链接 题意:训练指南P223 分析:二分长度,把所有字符串连成一个字符串,中间用不同的字符分隔(这是为了保证匹配长度始终在一个字符串内).height数组分段,vis数组标记哪些字符串被访问了,如 ...

  2. UVA11107 Life Forms --- 后缀数组

    UVA11107 Life Forms 题目描述: 求出出现在一半以上的字符串内的最长字符串. 数据范围: \(\sum len(string) <= 10^{5}\) 非常坑的题目. 思路非常 ...

  3. 后缀数组LCP + 二分 - UVa 11107 Life Forms

    Life Forms Problem's Link Mean: 给你n个串,让你找出出现次数大于n/2的最长公共子串.如果有多个,按字典序排列输出. analyse: 经典题. 直接二分判断答案. 判 ...

  4. POJ3294 Life Forms —— 后缀数组 最长公共子串

    题目链接:https://vjudge.net/problem/POJ-3294 Life Forms Time Limit: 5000MS   Memory Limit: 65536K Total ...

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

    题目链接: Poj 3294 Life Forms 题目描述: 有n个文本串,问在一半以上的文本串出现过的最长连续子串? 解题思路: 可以把文本串用没有出现过的不同字符连起来,然后求新文本串的heig ...

  6. POJ3294 Life Forms(后缀数组)

    引用罗穗骞论文中的话: 将n 个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组.然后二分答案,用和例3 同样的方法将后缀分成若干组,判断每组的后缀是否出现在不小于k 个的原串中 ...

  7. POJ-3294-Life Forms(后缀数组-不小于 k 个字符串中的最长子串)

    题意: 给定 n 个字符串,求出现在不小于 k 个字符串中的最长子串. 分析: 将 n 个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组. 然后二分答案,将后缀分成若干组,判断 ...

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

    [题目链接] http://poj.org/problem?id=3294 [题目大意] 求出在至少在一半字符串中出现的最长子串. 如果有多个符合的答案,请按照字典序输出. [题解] 将所有的字符串通 ...

  9. 2018.11.28 poj3294 Life Forms(后缀数组+双指针)

    传送门 后缀数组经典题目. 我们先把所有的字符串都接在一起. 然后求出hththt数组和sasasa数组. 然后对于sasasa数组跑双指针统计答案. 如果双指针包括进去的属于不同字符串的数量达到了题 ...

随机推荐

  1. 【BZOJ3098】 Hash Killer II

    BZOJ3098 Hash Killer II Solution 这道题目好像题面里面给了提示(当然没给就有点难想了.) 曾经讲过一个叫做生日悖论的,不知道还有多少人记得 考虑相同的可能性大概是\(\ ...

  2. 【Redis 向Redis中批量导入mysql中的数据(亲自测试)】

    转自:https://blog.csdn.net/kenianni/article/details/84910638 有改动,仅供个人学习 问题提出:缓存的冷启动问题 应用系统新版本上线,这时候 re ...

  3. PL/SQL中直接写SQL语句和用EXECUTE IMMEDIATE方法的区别

    PL/SQL中直接写SQL语句和用EXECUTE IMMEDIATE方法的区别 在PL/SQL中在执行SQL语句时可以直接写SQL或者可以把一个SQL语句拼成一个字符串,如下: select * fr ...

  4. A*算法解决15数码问题_Python实现

    1问题描述 数码问题常被用来演示如何在状态空间中生成动作序列.一个典型的例子是15数码问题,它是由放在一个4×4的16宫格棋盘中的15个数码(1-15)构成,棋盘中的一个单元是空的,它的邻接单元中的数 ...

  5. 笔记:Hive的主要技术改进(Major Technical Advancements in Apache Hive)

    http://web.cse.ohio-state.edu/hpcs/WWW/HTML/publications/papers/TR-14-2.pdf  (辅助参考:https://cwiki.apa ...

  6. mongodb 安装配置及简单使用

    步骤一: 下载网址:https://www.mongodb.com/download-center/community 根据自己的环境下载 步骤二: 安装过程只需要默认即可,需要注意的是连接工具“mo ...

  7. Oracle存储过程、游标、函数

    SQL99是什么 (1)是操作所有关系型数据库的规则 (2)是第四代语言 (3)是一种结构化查询语言 (4)只需发出合法合理的命令,就有对应的结果显示 SQL的特点 (1)交互性强,非过程化 (2)数 ...

  8. 关于微信XML解析存在的安全问题

    ---恢复内容开始--- 前言: 最近微信官方提出:微信支付商户,最近暴露的XML外部实体注入漏洞(XML External Entity Injection,简称 XXE),该安全问题是由XML组件 ...

  9. R语言与概率统计(二) 假设检验

    > ####################5.2 > X<-c(159, 280, 101, 212, 224, 379, 179, 264, + 222, 362, 168, 2 ...

  10. MATLAB学习(三)元素访问和常用代数运算

    >> A=[1,2;3,4],B=[0,2;4,5] A = 1 2 3 4 B = 0 2 4 5 >> C=A>=B C = 1 1 0 0 >> D=A ...