BZOJ 2946 SA/SAM

思路:

1. 二分+后缀数组

2.SAM

//By SiriusRen
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=22222;
int n,num,cntA[N],cntB[N],A[N],B[N],sa[N],rk[N],tsa[N],ht[N],rec[N],vis[10];
char ch[5555],s[N];
void SA(){
    for(int i=1;i<=n;i++)cntA[s[i]]++;
    for(int i=1;i<=256;i++)cntA[i]+=cntA[i-1];
    for(int i=n;i;i--)sa[cntA[s[i]]--]=i;
    rk[sa[1]]=1;
    for(int i=2;i<=n;i++)rk[sa[i]]=rk[sa[i-1]]+(s[sa[i]]!=s[sa[i-1]]);
    for(int l=1;rk[sa[n]]<n;l<<=1){
        memset(cntA,0,sizeof(cntA));
        memset(cntB,0,sizeof(cntB));
        for(int i=1;i<=n;i++)
            cntA[A[i]=rk[i]]++,
            cntB[B[i]=i+l<=n?rk[i+l]:0]++;
        for(int i=1;i<=n;i++)cntA[i]+=cntA[i-1],cntB[i]+=cntB[i-1];
        for(int i=n;i;i--)tsa[cntB[B[i]]--]=i;
        for(int i=n;i;i--)sa[cntA[A[tsa[i]]]--]=tsa[i];
        rk[sa[1]]=1;
        for(int i=2;i<=n;i++)
            rk[sa[i]]=rk[sa[i-1]]+(A[sa[i]]!=A[sa[i-1]]||B[sa[i]]!=B[sa[i-1]]);
    }
    for(int i=1,j=0;i<=n;i++){
        j=j?j-1:0;
        while(s[i+j]==s[sa[rk[i]-1]+j])j++;
        ht[rk[i]]=j;
    }
}
bool check(int mid){
    int cnt=0;
    for(int i=1;i<=n;i++){
        if(ht[i]<mid)memset(vis,0,sizeof(vis)),cnt=0;
        else{
            if(!vis[rec[sa[i]]])vis[rec[sa[i]]]=1,cnt++;
            if(!vis[rec[sa[i-1]]])vis[rec[sa[i-1]]]=1,cnt++;
        }
        if(cnt>=num)return 1;
    }return 0;
}
int main(){
    scanf("%d",&num);
    for(int i=1;i<=num;i++){
        scanf("%s",ch+1);
        int templen=strlen(ch+1);
        for(int j=1;j<=templen;j++)s[++n]=ch[j],rec[n]=i;
        s[++n]=i;
    }SA();
    int l=0,r=n,ans=0;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid))ans=mid,l=mid+1;
        else r=mid-1;
    }printf("%d\n",ans);
}
//By SiriusRen
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=4050;
int n,m;char s[N];
struct SAM{
    int ch[N][26],fa[N],dis[N],root,last,tot,ans[N],v[N],q[N],maxx[N];
    void init(){root=last=tot=1;}
    int newnode(int v){return dis[++tot]=v,tot;}
    void add(int x){
        int p=last,np=newnode(dis[p]+1);last=np;
        for(;p&&!ch[p][x];p=fa[p])ch[p][x]=np;
        if(!p)fa[np]=root;
        else{
            int q=ch[p][x];
            if(dis[q]==dis[p]+1)fa[np]=q;
            else{
                int nq=newnode(dis[p]+1);
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q],fa[np]=fa[q]=nq;
                for(;ch[p][x]==q;p=fa[p])ch[p][x]=nq;
            }
        }
    }
    void topsort(){
        for(int i=1;i<=tot;i++)ans[i]=dis[i],v[dis[i]]++;
        for(int i=1;i<=tot;i++)v[i]+=v[i-1];
        for(int i=tot;i;i--)q[v[dis[i]]--]=i;
    }
    void solve(){
        memset(maxx,0,sizeof(maxx));
        scanf("%s",s),n=strlen(s);
        int p=root,len=0;
        for(int i=0;i<n;i++){
            int x=s[i]-‘a‘;
            if(ch[p][x])len++,p=ch[p][x];
            else{
                for(;p&&!ch[p][x];p=fa[p]);
                if(!p)p=root,len=0;
                else len=dis[p]+1,p=ch[p][x];
            }maxx[p]=max(maxx[p],len);
        }
        for(int i=tot;i;i--){
            int x=q[i];
            ans[x]=min(ans[x],maxx[x]);
            if(maxx[x]&&fa[x])maxx[fa[x]]=max(maxx[fa[x]],dis[fa[x]]);
        }
    }
}T;
int main(){
    T.init();
    scanf("%d%s",&m,s),n=strlen(s);
    for(int i=0;i<n;i++)T.add(s[i]-‘a‘);
    T.topsort();
    for(int i=1;i<m;i++)T.solve();
    for(int i=1;i<=T.tot;i++)T.ans[1]=max(T.ans[1],T.ans[i]);
    printf("%d\n",T.ans[1]);
}
时间: 2024-11-08 22:12:27

BZOJ 2946 SA/SAM的相关文章

BZOJ 2946: [Poi2000]公共串

Description 最长公共子串,\(n\leqslant 5,l\leqslant 1000\) Solution SAM... 对于同一字符串取max,不用字符串取min Code /************************************************************** Problem: 2946 User: BeiYu Language: C++ Result: Accepted Time:1012 ms Memory:2308 kb ******

bzoj 2946

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

BZOJ 2946 Poi2000 公共串 后缀自动机

题目大意:求n个串的最长公共子串 太久没写SAM了真是-- 将第一个串建成后缀自动机,用其它的串进去匹配 每个节点记录每个串在上面匹配的最大长度 那么这个节点对答案的贡献就是所有最大长度的最小值 对所有贡献取最大就行了= = 这最大最小看着真是别扭 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 10100 using namesp

BZOJ 2946 POI2000 公共串 后缀自动机(多串最长公共子串)

题意概述:给出N个字符串,每个串的长度<=2000(雾...可能是当年的年代太久远机子太差了),问这N个字符串的最长公共子串长度为多少.(N<=5) 抛开数据结构,先想想朴素做法. 设计一种稳定的暴力算法.可以想到这样一种做法:首先确定一个串,枚举每个位置,然后暴力计算其他每个串以这个位置开头的最长匹配,取最小值,就是在公共子串在我们确定下来的串的这个位置开头的时候所能得到的最长公共子串.不难发现把这个问题转化成后缀的形式也是一样的.同时发现可能在枚举多个位置的时候答案甚至最后构造出来的串都是

【BZOJ】【2946】【POI2000】公共串

后缀数组 好感动,复习了下后缀数组居然写出来了……(感谢ykz大神) 求最长公共子串……WA了一发是因为:[不同字符串之间要用不同的特殊字符隔开]否则就会匹配到相同→_→比如都是aaa结尾,如果用相同特殊字符就会使得最长公共子串变成aaa#这样子…… 1 /************************************************************** 2 Problem: 2946 3 User: Tunix 4 Language: C++ 5 Result: Ac

目前的字符串

Aho-Corasick automaton:BZOJ4820[bzoj2754][SCOI2012]喵星球上的点名后缀数组:BZOJ4310 跳蚤manacher: BZOJ3160万径人踪灭Palindromic Tree:bzoj4044Bzoj3676:[Apio2014]回文串 bzoj 1559 ** ac+jvchengBZOJ1195:[HNOI2006]最短母串 ** ac+jvchengbzoj 1692 * sabzoj 1031 * sabzoj 3796 ** sa+k

PLAN OF HEOI(unfinished)

(忽略分组名称)Au:动态树分治/数位dp/博弈论/整体二分/杜教筛/计算几何/fft/ntt/fwtAg:可持久化重量平衡树/线段树分治/线段树合并/最短路树/最短路DAGCu:三分高:矩阵/行列式/矩阵树定理/(ex)BSGS/群论(Burnside引理/Polya定理......)/随机算法(爬山算法/模拟退火/随机增量法......)/生成函数/多项式/单纯形法解线性规划/原根强:网络流(线性规划/数据结构优化/NB模型......)/字符串算法(广义/可持久化......)/可持久化数

yyb要填的各种总结的坑

已经写好啦的 莫比乌斯反演 杜教筛 动态点分治 斜率优化 Splay 莫队 凸包 旋转卡壳 Manacher算法 Trie树 AC自动机 高斯消元 KMP算法 可以填的坑 [CF???] [Link-Cut Tree] [树链剖分] 要我填坑就催我把,要不然保证不填坑 我还不会的东西 [SA] [SAM] [带花树] 原文地址:https://www.cnblogs.com/cjyyb/p/8321222.html

ZJOI2018游记 [一试]

好快呀,ZJOI2018来了. 没时间磨蹭了,上火车-- Day -2 动车两个小时啊,好无聊啊. 和XZA一起玩密室逃脱[博士的家],真好玩.为什么要把钥匙扔在如此套路的地方. 菜鸡选手还是肛不过套路.赶紧百度,不然就真的GG了.哈,攻略在手,woc怎么把机关放在镜子后面,这博士有毒,还在墙上挖坑插钥匙??简直zz. 辣鸡游戏,居然玩了1个半小时才玩出. 才发现FestFy一直在好好学习. 这是要进队的节奏啊! 嘿嘿嘿 下动车打的到美林.陈皮糖真好吃.午饭真好吃. 据说猪好像在上火车的时候看到