您的位置:首页 > Web前端

UVa 11107 (后缀数组 二分) Life Forms

2015-04-20 16:58 239 查看
利用height值对后缀进行分组的方法很常用,好吧,那就先记下了。

题意:

给出n个字符串,求一个长度最大的字符串使得它在超过一半的字符串中出现。

多解的话,按字典序输出全部解。

分析:

在所有输入的字符串后面加一个原串中没有的且互不相同的字符,然后将新得到的n个字符串拼接成一个长的字符串。(为什么要加互不相同的分割字符,这里始终想不明白)

首先二分最大公共字串的长度p。扫描一遍height数组,每遇到一个height[i] < p便开辟一个新段,这样就将height数组拆分为若干段。而且每一段的所有字符都有一个长度为p的公共前缀。只要某一段中包含了超过 n / 2 的原串的后缀,就满足条件了。

如何判断是否包含了某个原串的后缀,用一个flag标记数组即可实现。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 1001 * 100 + 10;

struct SuffixArray
{
int s[maxn];
int sa[maxn];
int rank[maxn];
int height[maxn];
int t[maxn], t2[maxn], c[maxn];
int n;

void clear() { n = 0; memset(sa, 0, sizeof(sa)); }

void build_sa(int m)
{
int i, *x = t, *y = t2;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[i] = s[i]]++;
for(i = 1; i < m; i++) c[i] += c[i - 1];
for(i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
for(int k = 1; k <= n; k <<= 1)
{
int p = 0;
for(i = n - k; i < n; i++) y[p++] = i;
for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i] - k;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[y[i]]]++;
for(i = 1; i < m; i++) c[i] += c[i - 1];
for(i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1; x[sa[0]] = 0;
for(i = 1; i < n; i++)
x[sa[i]] = y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k] ? p - 1 : p++;
if(p >= n) break;
m = p;
}
}

void build_height()
{
int i, j, k = 0;
for(i = 0; i < n; i++) rank[sa[i]] = i;
for(i = 0; i < n; i++)
{
if(k) k--;
j = sa[rank[i] - 1];
while(s[i + k] == s[j + k]) k++;
height[rank[i]] = k;
}
}
};

const int maxc = 100 + 10;
const int maxl = 1000 + 10;
SuffixArray sa;
int n;
char word[maxl];
int idx[maxn];
bool flag[maxc];

void print_sub(int L, int R)
{
for(int i = L; i < R; i++) printf("%c", sa.s[i] - 1 + 'a');
puts("");
}

bool good(int L, int R)
{
memset(flag, false, sizeof(flag));
int cnt = 0;
for(int i = L; i < R; i++)
{
int x = idx[sa.sa[i]];
if(x != n && !flag[x]) { flag[x]  = true; cnt++; }
}
return cnt > n / 2;
}

bool print_solution(int len, bool print)
{
int L = 0;
for(int R = 1; R <= sa.n; R++)
{
if(R == sa.n || sa.height[R] < len)
{
if(good(L, R))
{
if(print) print_sub(sa.sa[L], sa.sa[L] + len);
else return true;
}
L = R;
}
}
return false;
}

void solve(int maxlen)
{
if(!print_solution(1, false)) puts("?");
else
{
int L = 1, R = maxlen, M;
while(L < R)
{
M = L + (R - L + 1) / 2;
if(print_solution(M, false)) L = M;
else R = M - 1;
}
print_solution(L, true);
}
}

void add(int ch, int i)
{
idx[sa.n] = i;
sa.s[sa.n++] = ch;
}

int main()
{
//freopen("in.txt", "r", stdin);

int kase = 0;
while(scanf("%d", &n) == 1 && n)
{
if(kase++ > 0) puts("");
sa.clear();
int maxlen = 0;
for(int i = 0; i < n; i++)
{
scanf("%s", word);
int sz = strlen(word);
maxlen = max(maxlen, sz);
for(int j = 0; j < sz; j++) add(word[j] - 'a' + 1, i);
add(i + 100, n);
}
add(0, n);

sa.build_sa(100 + n);
sa.build_height();
solve(maxlen);
}

return 0;
}


代码君
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: