[Poi2000]公共串

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cmath>
 4 #include <algorithm>
 5 #include <cstring>
 6 #define maxn 4005
 7 #define maxm 2005
 8 using namespace std;
 9
10 int n,m,tot,last,root,len,smin[maxn],sum[maxn],tmp[maxn],fa[maxn],son[maxn][26],dist[maxn];
11 char st[maxm];
12 struct Tsegment{
13     int ans[maxn];
14     void prepare(){tot=last=root=1;}
15     int newnode(int x){dist[++tot]=x;return tot;}
16     void add(int x){
17         int p=last,np=newnode(dist[p]+1); last=np;
18         for (;p&&!son[p][x];p=fa[p]) son[p][x]=np;
19         if (p==0) fa[np]=root;
20         else{
21             int q=son[p][x];
22             if (dist[p]+1==dist[q]) fa[np]=q;
23             else{
24                 int nq=newnode(dist[p]+1);
25                 memcpy(son[nq],son[q],sizeof(son[q]));
26                 fa[nq]=fa[q],fa[q]=fa[np]=nq;
27                 for (;p&&son[p][x]==q;p=fa[p]) son[p][x]=nq;
28             }
29         }
30     }
31     void Fsort(){
32         memset(sum,0,sizeof(sum));
33         for (int i=1;i<=tot;i++) ans[i]=dist[i];
34         for (int i=1;i<=tot;i++) sum[dist[i]]++;
35         for (int i=1;i<=tot;i++) sum[i]+=sum[i-1];
36         for (int i=1;i<=tot;i++) tmp[sum[dist[i]]--]=i;
37     }
38     void work(){
39         scanf("%s",st+1),m=strlen(st+1);int x;
40         memset(smin,0,sizeof(smin)),len=0,last=root;
41         for (int i=1;i<=m;i++){
42             x=st[i]-‘a‘;
43             if (son[last][x]) len++,last=son[last][x];
44             else{
45                 for (;last&&!son[last][x];) last=fa[last];
46                 if (last==0) len=0,last=root;
47                 else{
48                     len=dist[last]+1,last=son[last][x];
49                 }
50             }
51             smin[last]=max(smin[last],len);
52         }
53         for (int i=tot;i>=1;i--){
54             x=tmp[i];
55             ans[x]=min(ans[x],smin[x]);
56             if (fa[x]&&smin[x]) smin[fa[x]]=dist[fa[x]];
57         }
58     }
59 }SAM;
60
61 int main(){
62     int ans=0;
63     scanf("%d",&n),n--;
64     SAM.prepare();
65     scanf("%s",st+1),m=strlen(st+1);
66     for (int i=1;i<=m;i++) SAM.add(st[i]-‘a‘);
67     SAM.Fsort();
68     for (int i=1;i<=n;i++) SAM.work();
69     for (int i=1;i<=tot;i++) ans=max(ans,SAM.ans[i]);
70     printf("%d\n",ans);
71     return 0;
72 }

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2946

http://begin.lydsy.com/JudgeOnline/problem.php?id=2797

题目大意:给定n个字符串,n不大于5,每个字符串的长度不大于2000,求这n个字符串的最长公共子串的长度。

做法:首先,这题还是可以用后缀数组+二分答案解决,那后缀自动机怎么解决呢?

首先对其中一个串建立后缀自动机,其他串在上面匹配即可,失配则跳parent树。

对于多串,我们在每个节点上记录一个信息,表示所有匹配的字符串匹配到该节点是最小的长度,每次用该字符串在SAM上每个点的最大匹配长度去更新要维护的信息(最小的长度),原因自己YY一下即可,木桶效应嘛。

特别注意:在后缀自动机上匹配到一个节点时,那么它的所有祖先(通过fa一路可以到达的节点)都能匹配到其最大长度,每次记得通过一次dp得到。

后缀自动机。

时间: 2024-10-05 04:19:11

[Poi2000]公共串的相关文章

[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行每行一个单词

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

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

BZOJ 2946 Poi2000 公共串 后缀自动机

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

【BZOJ】2946: [Poi2000]公共串

http://www.lydsy.com/JudgeOnline/problem.php?id=2946 题意:给n个串,求最大公共子串.(1<=n<=5,每个串长度<=2000) #include <bits/stdc++.h> using namespace std; const int N=2005<<1; struct sam { int cnt, root, last, l[N], c[N][26], f[N], p[N], mx[N], mxl[N];

[Poi2000]公共串 &amp;&amp; hustoj2797

传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=2797 题目大意:给你几个串求出几个串中的最长公共子串. 题解:先看n最大才5,所以很容易想到暴力写法,因为最近在学后缀自动机就写写后缀自动机吧. 我们将第一个串作为母串,然后在用其他的串与它进行匹配,并且记录下其匹配中每个状态的最大匹配数,答案则为每个状态的最大匹配的最小值中的最大值..(绕晕了) 1 #include<iostream> 2 #include<algorith

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

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

[POI2000]公共串 - 后缀数组

Description 求若干个串的最长的公共子串的长度. Solution 考虑将这若干个串全部拼起来,中间用一些不在字符集内的符号隔开. 然后二分答案 \(K\),如果连续的一段 \(height\) 都大于等于 \(K\),且每个串都出现了至少一次,则是可行的. Code #include <bits/stdc++.h> using namespace std; const int _ = 1e5 + 10; int N, n, s[_], belong[_]; int rnk[_],

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 ******

【二分答案】【分块答案】【字符串哈希】【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