bzoj2946

后缀数组+二分

中间加个字符,然后二分判断即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100010;
int n, top, k, tot, m, ans;
int a[N], sa[N], rank[N], temp[N], lcp[N], belong[N];
bool flag[6];
char c[N], s[N];
bool cp(int i, int j)
{
    if(rank[i] != rank[j]) return rank[i] < rank[j];
    int ri = i + k <= n ? rank[i + k] : -1;
    int rj = j + k <= n ? rank[j + k] : -1;
    return ri < rj;
}
void Sa()
{
    for(int i = 1; i <= n; ++i)
    {
        sa[i] = i;
        rank[i] = s[i];
    }
    for(k = 1; k <= n; k <<= 1)
    {
        sort(sa + 1, sa + n + 1, cp);
        temp[sa[1]] = 1;
        for(int i = 2; i <= n; ++i) temp[sa[i]] = temp[sa[i - 1]] + (cp(sa[i - 1], sa[i]));
        for(int i = 1; i <= n; ++i) rank[i] = temp[i];
    }
}
void Lcp()
{
    for(int i = 1; i <= n; ++i) rank[sa[i]] = i;
    int h = 0;
    for(int i = 1; i <= n; ++i)
    {
        int j = sa[rank[i] - 1];
        if(rank[i] <= 1) continue;
        if(h) --h;
        for(; i + h <= n && j + h <= n; ++h) if(s[i + h] != s[j + h]) break;
        lcp[rank[i] - 1] = h;
    }
}
bool C(int x)
{
    for(int i = 1; i < n; ++i)
    {
        bool ff = true;
        if(lcp[i] >= x)
        {
            flag[belong[sa[i]]] = flag[belong[sa[i + 1]]] = true;
            for(int i = 1; i <= m; ++i) if(!flag[i])
            {
                ff = false;
                break;
            }
            if(ff) return true;
        }
        else memset(flag, false, sizeof(flag));
    }
    return false;
}
int main()
{
    scanf("%d", &m);
    for(int i = 1; i <= m; ++i)
    {
        scanf("%s", c + 1);
        for(int j = 1; j <= strlen(c + 1); ++j)
        {
            s[++n] = c[j];
            belong[n] = i;
        }
        s[++n] = ‘#‘ + i;
    }
    Sa();
    Lcp();
    int l = 0, r = 100000;
    while(r - l > 1)
    {
        int mid = (l + r) >> 1;
        if(C(mid)) l = ans = mid;
        else r = mid;
    }
    printf("%d\n", ans);
    return 0;
}

时间: 2024-08-28 00:55:39

bzoj2946的相关文章

[BZOJ2946] [Poi2000]公共串解题报告|后缀数组

给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 单词个数<=5,每个单词长度<=2000 尽管最近在学的是SAM...但是看到这个题还是忍不住想写SA... (其实是不知道应该怎么用SAM做... 对于后缀数组而言,多个字符串的公共子串与两个处理起来并没有什么区别 只要在中间加一些没有用的字符,将多个字符串拼成一个字符串 然后二分答案,对于一个长度L,在一组除了开头其他height都>=L的区间中如果每个字符串的位置都出现过就可以 应该是第二次这么解决一道公共串的题了.. 然

[BZOJ2946][Poi2000]公共串解题报告|后缀自动机

鉴于SAM要简洁一些...于是又写了一遍这题... 不过很好呢又学到了一些新的东西... 这里是用SA做这道题的方法 首先还是和两个字符串的一样,为第一个字符串建SAM 然后每一个字符串再在这个SAM上跑匹配 然而我们最后要的答案是什么呢? 是某个在所有字符串中匹配长度最小值最大的状态子串 然后对于每一个字符串 我们可以记录它在每一个状态子串上的最大匹配长度 最后需要一个非常关键的转移 就是用当前节点的值更新fail指针指向的节点 比如这种情况 如果一次匹配到左边的三个节点,一次匹配到右边的两个

【二分答案】【哈希表】【字符串哈希】bzoj2946 [Poi2000]公共串

二分答案,然后搞出hash值扔到哈希表里.期望复杂度O(n*log(n)). #include<cstdio> #include<cstring> #include<vector> using namespace std; typedef unsigned long long ull; const ull seed=29; #define MOD 2007 typedef vector<ull>::iterator VER; vector<ull>

BZOJ2946: [Poi2000]公共串

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2946 先用第一个字符串建后缀自动机,每个节点记录一下每个字符串与之匹配的最大值,那么每个节点对答案的贡献就是所记录的最大值的最小值,把所有刚刚说的最小值取max就是答案了.绕晕了.语文不好真是日了狗. 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define inf 1<&l

【二分答案】【分块答案】【字符串哈希】【set】bzoj2946 [Poi2000]公共串

我们二分/分块枚举答案x,暴力把除了最短的字符串以外的其他字符串的x长度子串哈希搞出来,分别扔到set里. 然后暴力枚举最短的字符串的x长度字串,查看是否在全部的set里出现过. #include<cstdio> #include<set> #include<cstring> #include<cmath> using namespace std; typedef unsigned long long ull; set<ull>T[6]; con

bzoj2946 [Poi2000]公共串(SA,SAM)

Description 给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l        读入单词 l        计算最长公共子串的长度 l        输出结果 Input 文件的第一行是整数 n,1<=n<=5,表示单词的数量.接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000. Output 仅一行,一个整数,最长公共子串的长度. Sample Input 3abcbbcaacbc Sample Output 2 [思路] 多串求LCS

【POI2000】【BZOJ2946】公共串

2946: [Poi2000]公共串 Time Limit: 3 Sec Memory Limit: 128 MB Submit: 286 Solved: 121 [Submit][Status][Discuss] Description 给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l 读入单词 l 计算最长公共子串的长度 l 输出结果 Input 文件的第一行是整数 n,1<=n<=5,表示单词的数量.接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大

【BZOJ2946】公共串 [SAM]

公共串 Time Limit: 3 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l 读入单词 l 计算最长公共子串的长度 l 输出结果 Input 文件的第一行是整数 n ,表示单词的数量.接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000. Output 仅一行,一个整数,最长公共子串的长度. Sample Input

[BZOJ2946][Poi2000]公共串 后缀自动机

2946: [Poi2000]公共串 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 1367  Solved: 612[Submit][Status][Discuss] Description 给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l        读入单词 l        计算最长公共子串的长度 l        输出结果 Input 文件的第一行是整数 n,1<=n<=5,表示单词的数量.接下来n行每行一个单词