【后缀自动机】SPOJ 1812-LCSII

题意:

  给出最多10个长度不超过100000的字符串,求他们的LCS的长度。时限是鬼畜的0.25s 。

后缀自动机练习...虽然有人这么说但我并不觉得hash能过。

本题可以说是【论SAM中按step排序更新pre的重要性】:

  总的来说做法和1811-LCS有点类似,不同的是因为有多个字符串,因此每个字符串都需要在SAM上跑一次。

  记录下每个节点最长能容纳长度为多少的字符,最后取个MAX就行了。

  用nans[i]表示匹配当前字符串时,i号点能容纳的子串长度,如果i的pre上的当前答案比i还要小的话就用nans[i]来更新nans[i.pre] 。

  用ans表示所有nans的最小值,在计算每个字符串时用nans来更新ans,最后在ans中找一个MAX就可以了。

一开始忘记了黑体的那句话,结果拍了很久又没拍出来,提交的时候在第10个点WA了简直难过...

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <iostream>
 5 #define maxn 105000*2
 6 using namespace std;
 7
 8 struct node { int s[26],pre; } Suf[maxn];
 9 char S[maxn],T[maxn];
10 int i,j,n,m,k,len1,len2,len,last = 1,root = 1,cnt = 1;
11 int step[maxn],nans[maxn],ans[maxn],f[maxn];
12
13 void add(int nxt)
14 {
15     int now = ++cnt, p = last;
16     step[now] = step[p] + 1;
17     for ( ; p != 0 && Suf[p].s[nxt] == 0; p = Suf[p].pre) Suf[p].s[nxt] = now;
18     if (p == 0) Suf[now].pre = root;
19     else
20     {
21         int q = Suf[p].s[nxt];
22         if (step[q] == step[p] + 1) Suf[now].pre = q;
23         else
24         {
25             int nq = ++cnt;
26             step[nq] = step[p] + 1;
27             Suf[nq] = Suf[q];
28             Suf[q].pre = Suf[now].pre = nq;
29             for ( ; p != 0 && Suf[p].s[nxt] == q; p = Suf[p].pre) Suf[p].s[nxt] = nq;
30         }
31     }
32     last = now;
33 }
34
35 bool cmp(int a,int b) { return step[a] < step[b]; }
36
37 int main()
38 {
39     scanf("%s\n",S+1);
40     len1 = strlen(S+1);
41     for (i = 1; i <= len1; i++) add(S[i]-‘a‘);
42
43     for (i = 1; i <= cnt; i++) f[i] = i;
44     stable_sort(f+1,f+1+cnt,cmp);
45
46     memcpy(ans,step,sizeof(ans));
47
48     while (scanf("%s\n",T+1) != EOF)
49     {
50         memset(nans,0,sizeof(nans));
51
52         len2 = strlen(T+1);
53         int now = root,len = 0;
54         for (i = 1; i <= len2; i++)
55         {
56             int nxt = T[i]-‘a‘;
57             if (Suf[now].s[nxt] != 0)
58             {
59                 len++;
60                 now = Suf[now].s[nxt];
61                 nans[now] = max(nans[now],len);
62             }
63             else
64             {
65                 while (Suf[now].s[nxt] == 0 && now != 0) now = Suf[now].pre;
66                 if (now == 0) now = root,len = 0;
67                 else len = step[now] + 1,now = Suf[now].s[nxt],nans[now] = max(nans[now],len);
68             }
69         }
70         for (i = cnt; i >= 1; i--)
71         {
72             ans[f[i]] = min(ans[f[i]],nans[f[i]]);
73             nans[Suf[f[i]].pre] = max(nans[Suf[f[i]].pre],nans[f[i]]);
74         }
75     }
76
77     int ANS = 0;
78     for (i = 1; i <= cnt; i++)
79         ANS = max(ANS,ans[i]);
80
81     printf("%d\n",ANS);
82     return 0;
83 }

SPOJ 1812

时间: 2024-10-06 13:27:23

【后缀自动机】SPOJ 1812-LCSII的相关文章

后缀自动机(SAM)学习指南

*在学习后缀自动机之前需要熟练掌握WA自动机.RE自动机与TLE自动机* 什么是后缀自动机 后缀自动机 Suffix Automaton (SAM) 是一个用 O(n) 的复杂度构造,能够接受一个字符串所有后缀的自动机. 它最早在陈立杰的 2012 年 noi 冬令营讲稿中提到. 在2013年的一场多校联合训练中,陈立杰出的 hdu 4622 可以用 SAM 轻松水过,由此 SAM 流行了起来. 一般来说,能用后缀自动机解决的问题都可以用后缀数组解决.但是后缀自动机也拥有自己的优点. 1812.

后缀自动机(SAM)

*在学习后缀自动机之前需要熟练掌握WA自动机.RE自动机与TLE自动机* 什么是后缀自动机 后缀自动机 Suffix Automaton (SAM) 是一个用 O(n) 的复杂度构造,能够接受一个字符串所有后缀的自动机. 它最早在陈立杰的 2012 年 noi 冬令营讲稿中提到. 在2013年的一场多校联合训练中,陈立杰出的 hdu 4622 可以用 SAM 轻松水过,由此 SAM 流行了起来. 一般来说,能用后缀自动机解决的问题都可以用后缀数组解决.但是后缀自动机也拥有自己的优点. 1812.

spoj 1812 LCS2 - Longest Common Substring II (后缀自动机)

spoj 1812 LCS2 - Longest Common Substring II 题意: 给出最多n个字符串A[1], ..., A[n], 求这n个字符串的最长公共子串. 限制: 1 <= n <= 10 |A[i]| <= 1e5 思路: 和spoj 1811 LCS差不多的做法 把其中一个A建后缀自动机 考虑一个状态s, 如果A之外的其他串对它的匹配长度分别是a[1], a[2], ..., a[n - 1], 那么min(a[1], a[2], ..., a[n - 1]

SPOJ 1812 Longest Common Substring II(后缀自动机)

[题目链接] http://www.spoj.com/problems/LCS2/ [题目大意] 求n个串的最长公共子串 [题解] 对一个串建立后缀自动机,剩余的串在上面跑,保存匹配每个状态的最小值, 取最小值中的最大值即可.由于跑的地方只记录了匹配结尾的状态, 所以还需要更新parent树上的状态,既然匹配到了子节点, 那么parent树链上的值就都能够取到l, 一开始给每个不同状态按照l从小到大分配储存地址, 这样,我们就可以从匹配长度最长的开始更新parent树的情况. [代码] #inc

SPOJ 1812 LCS2 - Longest Common Substring II (后缀自动机)【两种做法】

手动博客搬家: 本文发表于20181217 23:54:35, 原地址https://blog.csdn.net/suncongbo/article/details/85058680 人生第一道后缀自动机. 说实话SAM我还没学多么明白. 但是题还是要做的. 说起来这玩意真的很妙.可惜我智商低理解不了. 再次验证了代码能力菜到没边.hyw 30min写完我写2.5h. 题目链接 (洛谷) https://www.luogu.org/problemnew/show/SP1812 题目大意 给\(n

spoj 1811 LCS - Longest Common Substring (后缀自动机)

spoj 1811 LCS - Longest Common Substring 题意: 给出两个串S, T, 求最长公共子串. 限制: |S|, |T| <= 1e5 思路: dp O(n^2) 铁定超时 后缀数组 O(nlog(n)) 在spoj上没试过,感觉也会被卡掉 后缀自动机 O(n) 我们考虑用SAM读入字符串B; 令当前状态为s,同时最大匹配长度为len; 我们读入字符x.如果s有标号为x的边,那么s=trans(s,x),len = len+1; 否则我们找到s的第一个祖先a,它

【SPOJ】8222. Substrings(后缀自动机)

http://www.spoj.com/problems/NSUBSTR/ 题意:给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值.求F(1)..F(Length(S)) 这题做法: 首先建立字符串的后缀自动机. 因为自动机中的每个状态都代表一类子串前缀,且任意状态的最长的|max|所对应的子串是唯一的. 所以我们算出每个子串(即找到的状态是end态),他们的right值为++(即保证长度为当前子串的出现次数为1),然后自底向上在parent树中更新right值(pare

【后缀自动机】SPOJ 8222-最多重复子串

题意: 一个长度不超过250000的字符串,求出它长度为i的子串最多出现了多少次. 后缀自动机练习题...虽说是用Cube评测的不过时限仍然鬼畜. 考虑SAM的性质: 一个串的SAM肯定可以接受这个串的所有子串.SAM上的每一个点代表了一个子串. 主链:SAM上最长的那条链,也就是说从根走到主链的尾端就是整个字符串. 用t[i]记录i号点表示的子串的出现次数,沿着主链走一遍,很明显,主链上所有的点都代表了一个前缀,每个前缀都出现过.因此主链上的点的t初始值为1. 接着,之前提到过,一个点的pre

spoj 1812 LCS2 - Longest Common Substring II (后缀自己主动机)

spoj 1812 LCS2 - Longest Common Substring II 题意: 给出最多n个字符串A[1], ..., A[n], 求这n个字符串的最长公共子串. 限制: 1 <= n <= 10 |A[i]| <= 1e5 思路: 和spoj 1811 LCS几乎相同的做法 把当中一个A建后缀自己主动机 考虑一个状态s, 假设A之外的其它串对它的匹配长度各自是a[1], a[2], ..., a[n - 1], 那么min(a[1], a[2], ..., a[n -