cogs249 最长公共子串(后缀数组 二分答案

http://cogs.pro:8080/cogs/problem/problem.php?pid=pxXNxQVqP

题意:给m个单词,让求最长公共子串的长度。

思路:先把所有单词合并成一个串(假设长度是n,包含分隔符),中间用不同符号分隔,求出high[i](表示rk为i的和rk为i+1的后缀的最长公共前缀),然后二分答案ans,对于rk从1扫到n,如果有一段连续的rk值使得high[rk]>=ans且这段的串盖满了每个单词块,那么ans成立,即最终答案大于ans。

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int N=20005;
int s[N],n=0,k,tmp[N],sa[N],rk[N],high[N],color[N],m,l[6];char S[6][2005];
bool comp(int i,int j){
    if(rk[i]!=rk[j])return rk[i]<rk[j];
    int ri=i+k<=n?rk[i+k]:-1;
    int rj=j+k<=n?rk[j+k]:-1;
    return ri<rj;
}
void Getsa(){
    for(int i=1;i<=n;i++){
        sa[i]=i;rk[i]=s[i];
    }
    for(k=1;k<=n;k<<=1){
        sort(sa+1,sa+n+1,comp);
        for(int i=1;i<=n;i++)tmp[sa[i]]=tmp[sa[i-1]]+comp(sa[i-1],sa[i]);
        for(int i=1;i<=n;i++)rk[i]=tmp[i];
    }
}
void Gethight(){
    int j,h=0;
    for(int i=1;i<=n;i++){
        j=sa[rk[i]-1];
        if(h)h--;
        for(;j+h<=n && i+h<=n;h++)if(s[i+h]!=s[j+h])break;
        high[rk[i]-1]=h;
    }
}
void prework(int m){
    int p=0;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=l[i];j++)
            p++,color[p]=i;
        p++;
    }
}
bool d[8];
bool check(int lim){
    int p,ret;
    for(int i=1;i<n;i++){
        if(high[i]>=lim){
            p=i;
            while(p<n && high[p+1]>=lim)p++;p++;
            for(int j=1;j<=m;j++)d[j]=false;
            for(int j=i;j<=p;j++)d[color[sa[j]]]=true;
            ret=p;p=1;
            while(p<=m && d[p])p++;
            if(p==m+1)return true;
            i=ret;
        }
    }
    return false;
}
int main()
{
    freopen("pow.in","r",stdin);
    freopen("pow.out","w",stdout);
    int r=2e8;
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%s",S[i]);
        l[i]=strlen(S[i]);
        if(l[i]<r)r=l[i];
        for(int j=0;j<l[i];j++)s[++n]=S[i][j];
        s[++n]=i;
    }
    Getsa();Gethight();prework(m);
    int l=0,mid,ans;
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid))ans=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/wzgg/p/11421390.html

时间: 2024-07-30 17:02:05

cogs249 最长公共子串(后缀数组 二分答案的相关文章

hdu 4691 最长公共前缀 后缀数组 +lcp+rmq

http://acm.hdu.edu.cn/showproblem.php?pid=4691 去年暑假多校赛的题,当时还不会后缀数组 现在会了,其实自己组合后缀数组跟rmq还是对的,但是题意理解有问题,于是折腾了很久,,,, 此处简单解释下题目样例吧,希望对读者有帮助  以最后一组数据为例 myxophytamyxopodnabnabbednabbingnabit 6 0 9 9 16 16 19 19 25 25 32 32 37 前两行不解释,题目叙述很清楚 从第三行,0 9 指的是第一个字

POJ 3080 Blue Jeans(后缀数组+二分答案)

[题目链接] http://poj.org/problem?id=3080 [题目大意] 求k个串的最长公共子串,如果存在多个则输出字典序最小,如果长度小于3则判断查找失败. [题解] 将所有字符串通过拼接符拼成一个串,做一遍后缀数组,二分答案,对于二分所得值,将h数组大于这个值的相邻元素分为一组,判断组内元素是否覆盖全字典,是则答案成立,对于答案扫描sa,输出第一个扫描到的子串即可. [代码] #include <cstdio> #include <cstring> #inclu

POJ3294--Life Forms 后缀数组+二分答案 大于k个字符串的最长公共子串

Life Forms Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 10800   Accepted: 2967 Description You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial traits such as height, colour, wrinkles, e

BZOJ 3230 相似子串 | 后缀数组 二分 ST表

BZOJ 3230 相似子串 题面 题解 首先我们要知道询问的两个子串的位置. 先正常跑一遍后缀数组并求出height数组. 对于每一个后缀suffix(i),考虑以i开头的子串有多少是之前没有出现过的,也就是考虑左端点在i.右端点在什么范围内时这个子串没有出现过--答案是右端点在[i + height[i] - 1, n]范围内时这个子串没出现过,即右端点在没有被"i与排在前一个的后缀的公共前缀"覆盖的部分时,这个子串没有出现过. 那么我们记录以每个i开头的新子串的数量,求前缀和,然

poj1743 后缀数组+二分答案

1.给定一个字符串,求最长重复子串,这两个子串可以重叠. 这道题是后缀数组的一个简单应用.做法比较简单,只需要求 height 数组里的最大值即可.2.给定一个字符串,求最长重复子串,这两个子串不能重叠. 这题比上一题稍复杂一点.先二分答案,把题目变成判定性问题:判断是否存在两个长度为 k 的子串是相同的,且不重叠.解决这个问题的关键还是利用height 数组.把排序后的后缀分成若干组,其中每组的后缀之间的 height 值都不小于 k.例如,字符串为“aabaaaab ” ,当 k=2 时,后

POJ 1743 Musical Theme(后缀数组+二分答案)

[题目链接] http://poj.org/problem?id=1743 [题目大意] 给出一首曲子的曲谱,上面的音符用不大于88的数字表示, 现在请你确定它主旋律的长度,主旋律指的是出现超过一次, 并且长度不小于5的最长的曲段,主旋律出现的时候并不是完全一样的, 可能经过了升调或者降调,也就是说, 是原来主旋律所包含的数字段同时加上或者减去一个数所得, 当然,两段主旋律之间也是不能有重叠的,现在请你求出这首曲子主旋律的长度, 如果不存在请输出0. [题解] 首先要处理的是升调和降调的问题,由

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

[题目链接] http://poj.org/problem?id=3294 [题目大意] 求出在至少在一半字符串中出现的最长子串. 如果有多个符合的答案,请按照字典序输出. [题解] 将所有的字符串通过不同的拼接符相连,作一次后缀数组, 二分答案的长度,然后在h数组中分组,判断是否可行, 按照sa扫描输出长度为L的答案即可.注意在一个子串中重复出现答案串的情况. [代码] #include <cstdio> #include <cstring> #include <vecto

POJ 3261 Milk Patterns(后缀数组+二分答案)

[题目链接] http://poj.org/problem?id=3261 [题目大意] 求最长可允许重叠的出现次数不小于k的子串. [题解] 对原串做一遍后缀数组,二分子串长度x,将前缀相同长度超过x的后缀分组, 如果存在一个大小不小于k的分组,则说明答案可行,分治得到最大可行解就是答案. [代码] #include <cstdio> #include <cstring> #include <vector> using namespace std; const int

SPOJ 220 Relevant Phrases of Annihilation(后缀数组+二分答案)

[题目链接] http://www.spoj.pl/problems/PHRASES/ [题目大意] 求在每个字符串中出现至少两次的最长的子串 [题解] 注意到这么几个关键点:最长,至少两次,每个字符串. 首先对于最长这个条件,我们可以想到二分答案, 然后利用后缀数组所求得的三个数组判断是否满足条件. 其次是出现两次,每次出现这个条件的时候, 我们就应该要想到这是最大值最小值可以处理的, 将出现在同一个字符串中的每个相同字符串的起始位置保存下来, 如果最小值和最大值的差距超过二分长度L,则表明在