[Poi2000]公共串 && hustoj2797

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

题目大意:给你几个串求出几个串中的最长公共子串。

题解:先看n最大才5,所以很容易想到暴力写法,因为最近在学后缀自动机就写写后缀自动机吧。

   我们将第一个串作为母串,然后在用其他的串与它进行匹配,并且记录下其匹配中每个状态的最大匹配数,答案则为每个状态的最大匹配的最小值中的最大值。。(绕晕了)

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

注意:我们在写的过程中,每到一个状态我们要不断跟新之前的状态答案,我们可以用dfs或者利用right数组的特性来更新,具体细节看看代码。

时间: 2024-11-08 20:20:05

[Poi2000]公共串 && hustoj2797的相关文章

[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

[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[m

【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];

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