题目链接:http://poj.org/problem?id=3294

题意:给定n个字符串,求一个最长子串要求在超过一半的字符串中出现过。 如果多解按字典序输出

思路:根据<<后缀数组——处理字符串的有力工具>>的思路,将 n个字符串连起来, 中间用不相同的且没有出现在字符串中的字符隔开, 求后缀数组。然后二分答案,把题目变成判定性问题:判断是否存在两个长度为k 的子串是相同的,且不重叠。解决这个问题的关键还是利用height 数组。把排序后的后缀分成若干组,其中每组的后缀之间的height 值都不小于k。的方法将后缀分成者干组,判断每组的后缀是:含出現在不小于 k个的原串中 。 在判断时,可能存在多组分组满足条件,那么对于每个满足条件的分组的任意一个sa[i]保存下来,因为枚举是按sa[i]递增所以结果的字典序也是满足递增的,这个做法的时间复杂度为 0(nlogn) 。

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<time.h>
#include<cmath>
#include<stack>
using namespace std;
typedef long long int LL;
const int MAXN = * * ;
int wa[MAXN], wb[MAXN], wv[MAXN], WS[MAXN];
int cmp(int *r, int a, int b, int l)
{
return r[a] == r[b] && r[a + l] == r[b + l];
}
void da(int *r, int *sa, int n, int m)
{
int i, j, p, *x = wa, *y = wb, *t;
for (i = ; i<m; i++) WS[i] = ;
for (i = ; i<n; i++) WS[x[i] = r[i]]++;
for (i = ; i<m; i++) WS[i] += WS[i - ];
for (i = n - ; i >= ; i--) sa[--WS[x[i]]] = i;
for (j = , p = ; p<n; j *= , m = p)
{
for (p = , i = n - j; i<n; i++) y[p++] = i;
for (i = ; i<n; i++) if (sa[i] >= j) y[p++] = sa[i] - j;
for (i = ; i<n; i++) wv[i] = x[y[i]];
for (i = ; i<m; i++) WS[i] = ;
for (i = ; i<n; i++) WS[wv[i]]++;
for (i = ; i<m; i++) WS[i] += WS[i - ];
for (i = n - ; i >= ; i--) sa[--WS[wv[i]]] = y[i];
for (t = x, x = y, y = t, p = , x[sa[]] = , i = ; i<n; i++)
x[sa[i]] = cmp(y, sa[i - ], sa[i], j) ? p - : p++;
}
return;
}
int Rank[MAXN], height[MAXN], sa[MAXN];
void calheight(int *r, int *sa, int n){
int i, j, k = ;
for (i = ; i <= n; i++) { Rank[sa[i]] = i; }
for (i = ; i < n; height[Rank[i++]] = k){
for (k ? k-- : , j = sa[Rank[i] - ]; r[i + k] == r[j + k]; k++);
}
return;
}
int r[MAXN], len, n, vis[],index[MAXN];
//vis:标记数组 index:记录拼接后的字符串每个位置属于原输入的哪个串
char str[MAXN], sub[];
vector<int>ans; //保存多个结果的起点
bool check(int x){
int cnt = ;
bool flag = false; //是否存在答案
memset(vis, , sizeof(vis));
for (int i = ; i < len; i++){
if (i == len - ){
if (cnt > n / ){ //出现超过一半的字符串中
if (!flag){ ans.clear(); } //有新答案清空旧答案
ans.push_back(sa[i - ]);
flag = true;
}
break;
}
if (height[i] >= x){ //后缀出现的位置
if (!vis[index[sa[i - ]]]){ vis[index[sa[i - ]]] = ; cnt++; }
if (!vis[index[sa[i]]]){ vis[index[sa[i]]] = ; cnt++; }
}
else{
if (cnt > n / ){ //出现超过一半的字符串中
if (!flag){ ans.clear(); }
ans.push_back(sa[i - ]);
flag = true;
}
memset(vis, , sizeof(vis));
cnt = ;
}
}
return flag;
}
void solve(){ //二分求解
ans.clear();
int L = , R = , mid;
while (R >= L){
mid = (L + R) / ;
if (check(mid)){
L = mid + ;
}
else{
R = mid - ;
}
}
if (R < ){
printf("?\n");
}
else{ //因为是按sa递增求解的,所以答案保存的起点都是字典序递增的
for (int i = ; i < ans.size(); i++){
for (int j = ; j < R; j++){
printf("%c", str[ans[i] + j]);
}
printf("\n");
}
}
printf("\n");
}
int main(){
//#ifdef kirito
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
//#endif
// int start = clock();
while (scanf("%d", &n) && n){
len = ;
for (int i = , val = ; i <= n; i++, val++){
scanf("%s", &sub);
for (int j = ; j < strlen(sub); j++){
index[len] = i;
r[len++] = (sub[j] - 'a' + n + );
//因为n个字符串要用n个比输入字符要小的特殊字符隔开,
//所以a映射成n+1
}
index[len] = i;
r[len++] = val;
if (i == ){strcpy(str, sub);}
else{strcat(str, sub);}
strcat(str, "#");
}
da(r, sa, len, );
calheight(r, sa, len - );
solve();
}
//#ifdef LOCAL_TIME
// cout << "[Finished in " << clock() - start << " ms]" << endl;
//#endif
return ;
}

POJ 3294 后缀数组的更多相关文章

  1. poj 3294 后缀数组 多字符串中不小于 k 个字符串中的最长子串

    Life Forms Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 16223   Accepted: 4763 Descr ...

  2. poj 3294 后缀数组+二分

    题目大意: 给定n个字符串,求出现在不小于k个字符串中的最长子串 基本思路: 二分长度,统计个数,一般套路,就是这个跟说好的不一样啊,我非得开2倍才不re,真他妈不爽,先二分找出长度,然后根据长度输出 ...

  3. poj 3693 后缀数组 重复次数最多的连续重复子串

    Maximum repetition substring Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8669   Acc ...

  4. POJ - 3294~Relevant Phrases of Annihilation SPOJ - PHRASES~Substrings POJ - 1226~POJ - 3450 ~ POJ - 3080 (后缀数组求解多个串的公共字串问题)

    多个字符串的相关问题 这类问题的一个常用做法是,先将所有的字符串连接起来, 然后求后缀数组 和 height 数组,再利用 height 数组进行求解. 这中间可能需要二分答案. POJ - 3294 ...

  5. POJ 3415 后缀数组

    题目链接:http://poj.org/problem?id=3415 题意:给定2个串[A串和B串],求两个串公共子串长度大于等于k的个数. 思路:首先是两个字符串的问题.所以想用一个'#'把两个字 ...

  6. POJ 3450 后缀数组/KMP

    题目链接:http://poj.org/problem?id=3450 题意:给定n个字符串,求n个字符串的最长公共子串,无解输出IDENTITY LOST,否则最长的公共子串.有多组解时输出字典序最 ...

  7. POJ 1226 后缀数组

    题目链接:http://poj.org/problem?id=1226 题意:给定n个字符串[只含大小写字母],求一个字符串要求在n个串或者他们翻转后的串的出现过.输出满足要求的字符串的长度 思路:根 ...

  8. POJ 2774 后缀数组

    题目链接:http://poj.org/problem?id=2774 题意:给定两个只含小写字母的字符串,求字符串的最长公共子串长度. 思路:根据<<后缀数组——处理字符串的有力工具&g ...

  9. POJ 3693 后缀数组

    题目链接:http://poj.org/problem?id=3693 题意:首先定义了一个字符串的重复度.即一个字符串由一个子串重复k次构成.那么最大的k即是该字符串的重复度.现在给定一个长度为n的 ...

随机推荐

  1. SQL键值约束、索引使用

    添加約束的方式: [exec sp_helpconstraint 表名]->可用于查找到表创建的约束 CREATE TABLE stuInfo ( stuName ) NOT NULL,非空約束 ...

  2. HDU 4811 Ball -2013 ICPC南京区域现场赛

    题目链接 题意:三种颜色的球,现给定三种球的数目,每次取其中一个放到桌子上,排成一条线,每次放的位置任意,问得到的最大得分. 把一个球放在末尾得到的分数是它以前球的颜色种数 把一个球放在中间得到的分数 ...

  3. 51nod 1070 Bash游戏 V4 (斐波那契博弈)

    题目:传送门. 有一堆个数为n(n>=2)的石子,游戏双方轮流取石子,规则如下: 1)先手不能在第一次把所有的石子取完,至少取1颗: 2)之后每次可以取的石子数至少为1,至多为对手刚取的石子数的 ...

  4. [Android Pro] Android 6.0 Root

    reference : http://bbs.gfan.com/android-8001827-1-1.html 2. 刷机和刷入recovery 建议参考 置顶教程: [15年3月31日更新]Nex ...

  5. Android 录音

    想要实现wav格式的编码时我们也就不能再使用MediaRecorder,而只能使用AudioRecord进行处理

  6. vector< vector<int> >类似于二维数组

    vector< vector<int> > intVV; vector<int> intV; int i,j; ;i<;++i){ intV.clear(); ...

  7. hdu 1711Number Sequence

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1711 数字KMP,原来还能这么用 #include<stdio.h> ],b[]; ]; ...

  8. 攻城狮在路上(壹) Hibernate(十一)--- 映射实体关联关系

    本文以Customer和Address类的关系为例说明一对一关联映射:以Category和Item类的关系说明多对多关联关系.一.映射一对一关联: 分两种情况:按照外键映射和按照主键映射.这两种方式的 ...

  9. PHPCMS-V9 获取一级栏目、二级栏目、三级栏目

    <!-- 获取一级栏目 -->{pc:content action=" siteid="$siteid" order="listorder ASC& ...

  10. RTCP资料详解

    转自:http://www.360doc.com/content/13/0606/10/1317564_290865866.shtml RTCP RTCP协议将控制包周期发送给所有连接者,应用与数据包 ...