【UVA11107 训练指南】Life Forms【后缀数组】
题意
输入n(n<=100)个字符串,每个字符串长度<=1000,你的任务是找出一个最长的字符串使得超过一半的字符串都包含这个字符串。
分析
训练指南上后缀数组的一道例题,据说很经典(估计也就是height分组比较常用)。但是训练指南上给出的中文题面真滴坑B啊!书上说,连续出现,我懵逼了好久!
我们把这n个字符串连成一个长的字符串S,且中间用不同的未出现的字符相隔开(为什么隔开我们后面说),比如样例一会变为abcdefg1bcdefgh2cdefghi3。这样每一段是一个原字符串。然后问题转换为,在S字符串中,找出一个最长的字符串,使得至少有n/2的匹配点与这个最长的字符串相匹配,且匹配点分别位于至少n/2段。
求最长的时候我们二分答案,然后判断mid是否满足条件。关键是如何判断。我们回想一下通过height数组求LCP的方法,我们知道LCP(i,j)=RMQ(height,rank[i]+1,rank[j]).这里和这个原理是类似的。我们遍历height数组,当height[i]<mid的时候,就新增一个分组。也就是每个分组内的LCP都是>=mid的。然后判断一下这个分组内的后缀是否符合上面说的那个条件(既分组内至少有n/2个后缀,且后缀分别位于至少n/2段)。如果符合则说明mid是合法的。我们上面说过,不同的原串我们在合成S的时候,之间是用特殊字符隔开的,就是为了保证这里每个LCP都是在一个段内。
当时我还在纠结过,这样只是求出最长长度,但是题目要求输出啊。其实,你只要再跑一下上面判断的过程,把那些符合条件的LCP都输出出来就可以了。
mmp宿舍又又又半夜停电还特么这么多蚊子,感觉最后被劝退ACM的不是自己的菜而是暑假集训期间宿舍的温度····
下面是ac的code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream> using namespace std;
const int maxn=+;
const int maxs=+;
char s[maxn*maxs];
int c[maxn*maxs],t[maxn*maxs],t2[maxn*maxs],sa[maxn*maxs];
int n,N;
void build_sa(int m){
int *x=t,*y=t2;
for(int i=;i<m;i++)c[i]=;
for(int i=;i<n;i++)c[x[i]=s[i]]++;
for(int i=;i<m;i++)c[i]+=c[i-];
for(int i=n-;i>=;i--)sa[--c[x[i]]]=i; for(int k=;k<=n;k<<=){
int p=;
for(int i=n-k;i<n;i++)y[p++]=i;
for(int i=;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
for(int i=;i<m;i++)c[i]=;
for(int i=;i<n;i++)c[x[y[i]]]++;//这里为啥是这样?一会结合基数排序想一下
for(int i=;i<m;i++)c[i]+=c[i-];
for(int i=n-;i>=;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=,x[sa[]]=;
for(int i=;i<n;i++)
x[sa[i]]=y[sa[i]]==y[sa[i-]]&&y[sa[i]+k]==y[sa[i-]+k]?p-:p++;
if(p>=n)break;
m=p;
}
} int Rank[maxn*maxs],height[maxn*maxs];
void getHeight(){//这里有个bug,sa[0]不能等于0
int k=;
for(int i=;i<n;i++)Rank[sa[i]]=i;
for(int i=;i<n;i++){
if(i==&&Rank[i]==)
continue;
if(k)k--;
int j=sa[Rank[i]-];
while(s[i+k]==s[j+k])k++;
height[Rank[i]]=k;
}
} char word[maxs];
int idx[maxn*maxs];
int maxlen;
void add(int len,int id){
for(int i=;i<len;i++){
idx[n]=id;
s[n++]=word[i]-'a';
}
s[n]=+id;
idx[n++]=N;
} bool good(int L,int R){
if(R-L+<=N/)
return false;
int res=;
int vis[maxn];
memset(vis,,sizeof(vis));
for(int i=L;i<=R;i++){
int id=idx[sa[i]];
if(!vis[id]&&id!=N){
vis[id]=;
res++;
}
}
if(res>N/)
return true;
return false;
} bool judge(int mid){
int L=;
for(int R=;R<n;R++){
if(height[R]<mid){
if(good(L,R-)){
return true;
}
L=R;
}
}
return false;
} void print(int mid){
int L=;
for(int R=;R<n;R++){
if(height[R]<mid){
if(good(L,R-)){
for(int i=;i<mid;i++){
printf("%c",s[i+sa[L]]+'a');
}
printf("\n");
}
L=R;
}
}
}
int main(){
int kase=;
while(scanf("%d",&N)!=EOF&&N){
if(kase)printf("\n");
kase=;
n=;
for(int i=;i<N;i++){
scanf("%s",word);
int len=strlen(word);
maxlen=max(maxlen,len);
add(len,i);
}
if(N==){
printf("%s\n",word);
continue;
}
build_sa(+N);
getHeight();
int L=,R=maxlen;
int ans=-;
while(L<=R){
int M=L+(R-L)/;
if(judge(M))
{
ans = M;
L=M+;
}
else
R=M-;
}
if(ans==-){
printf("?\n");
continue;
}
print(ans);
}
return ;
}
【UVA11107 训练指南】Life Forms【后缀数组】的更多相关文章
- UVA11107 Life Forms --- 后缀数组
UVA11107 Life Forms 题目描述: 求出出现在一半以上的字符串内的最长字符串. 数据范围: \(\sum len(string) <= 10^{5}\) 非常坑的题目. 思路非常 ...
- POJ3294 Life Forms —— 后缀数组 最长公共子串
题目链接:https://vjudge.net/problem/POJ-3294 Life Forms Time Limit: 5000MS Memory Limit: 65536K Total ...
- Poj 3294 Life Forms (后缀数组 + 二分 + Hash)
题目链接: Poj 3294 Life Forms 题目描述: 有n个文本串,问在一半以上的文本串出现过的最长连续子串? 解题思路: 可以把文本串用没有出现过的不同字符连起来,然后求新文本串的heig ...
- 2016多校联合训练4 F - Substring 后缀数组
Description ?? is practicing his program skill, and now he is given a string, he has to calculate th ...
- POJ3294 Life Forms(后缀数组)
引用罗穗骞论文中的话: 将n 个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组.然后二分答案,用和例3 同样的方法将后缀分成若干组,判断每组的后缀是否出现在不小于k 个的原串中 ...
- POJ 3294 UVA 11107 Life Forms 后缀数组
相同的题目,输出格式有区别. 给定n个字符串,求最长的子串,使得它同时出现在一半以上的串中. 不熟悉后缀数组的童鞋建议先去看一看如何用后缀数组计算两个字符串的最长公共子串 Ural1517 这道题的思 ...
- POJ3294--Life Forms 后缀数组+二分答案 大于k个字符串的最长公共子串
Life Forms Time Limit: 500 ...
- POJ 3294 Life Forms 后缀数组+二分 求至少k个字符串中包含的最长子串
Life Forms Description You may have wondered why most extraterrestrial life forms resemble humans, ...
- poj 3294 Life Forms - 后缀数组 - 二分答案
题目传送门 传送门I 传送门II 题目大意 给定$n$个串,询问所有出现在严格大于$\frac{n}{2}$个串的最长串.不存在输出'?' 用奇怪的字符把它们连接起来.然后求sa,hei,二分答案,按 ...
随机推荐
- linux安装json
安装Json库 1.下载JsonCpphttp://sourceforge.net/projects/jsoncpp/files/ 2.下载sconshttp://sourceforge.net/pr ...
- boosting_bagging
boosting(提升法) 对于训练集中的每个样本建立全职W(i),当某个样本被错误分类概率很高时,样本的权重加大: 在迭代过程中,每一个迭代器都是一个弱分类器,我们需要用某种策略将其组合,作为最终模 ...
- 设计神器 - 摹客设计系统上线了 | 晒出你的设计规范,赢iPad Pro!
在国内,设计规范也许还是个不太常用的概念,但是如果你正好有参与互联网公司的产品设计,你应该早就已经体会到设计规范的重要性了.UI设计师总是要花费大量的时间和精力向开发描述一大堆设计细节,但是产品最后呈 ...
- c++11多线程学习笔记之一 thread基础使用
没啥好讲的 c++11 thread类的基本使用 #include "stdafx.h" #include <iostream> #include <thre ...
- Python鸭子类型思想
动态语言中经常提到鸭子类型,所谓鸭子类型就是:如果走起路来像鸭子,叫起来也像鸭子,那么它就是鸭子(If it walks like a duck and quacks like a duck, it ...
- const当做标记的函数重载,但是仅仅是限于类里面的成员函数
(1)我们知道函数的重载时根据函数的参数类型以及函数参数个数来重载的,不能用函数返回值来重载函数.但是有时候函数参数个数和函数参数类型重载函数会和默认参数发生冲突: int fun(int i,cha ...
- 流程控制 if while for 已及数字类型 进制转换
一:if 语句 语法一:ifif 条件: code1 code1 code1 语法二:if ...else ... if 条件: code1 code1 code1else: code1 code1 ...
- python面向对象开发的自我理解
详细代码理解可以参考 笨鸟教程博客:http://www.runoob.com/python3/python3-class.html 面向对象经常被提起,那到底什么是面向对象呢? 它的基本概念:类, ...
- HDU 2138 How many prime numbers (判素数,米勒拉宾算法)
题意:给定一个数,判断是不是素数. 析:由于数太多,并且太大了,所以以前的方法都不适合,要用米勒拉宾算法. 代码如下: #include <iostream> #include <c ...
- 如何使用 Visual C# 2005 或 Visual C# .NET 向 Excel 工作簿传输数据
本文分步介绍了多种从 Microsoft Visual C# 2005 或 Microsoft Visual C# .NET 程序向 Microsoft Excel 2002 传输数据的方法.本文还提 ...