【SAM】codevs3160-最长公共子串

【题目大意】

求两个字符串的最长公共子串。

【思路】

对第一个字符串建立后缀自动机,第二个字符串去匹配。cnt记录当前最长公共子串的长度,而ret记录答案。

p代表位置指针,初始在rt位置。

对于第二个字符串的某一位s[i],如果当前有s[i]孩子,则cnt+1,继续往后移动;否则沿着pre指针返回。如果pre指针返回到0,则将p回到rt,cnt清空为0;否则如果中间有点拥有s[i]孩子,cnt=step[]+1。

为什么cnt=step[]+1?不要忘了后缀自动机的本质是维护后缀,沿着pre指针跑就是往长度更小的后缀移动,某位置代表的后缀的最长长度为step[],再加上s[i],即是step[]+1。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 const int MAXN=100000+5;
 8 int n;
 9 char str[2][MAXN];
10 struct SAM
11 {
12     int step[MAXN*2],pre[MAXN*2],next[MAXN*2][26];
13     int tot,last;
14     inline int newNode(int cnt)
15     {
16         step[++tot]=cnt;
17         pre[tot]=0;
18         for (int i=0;i<26;i++) next[tot][i]=0;
19         return tot;
20     }
21
22     inline void extend(int x)
23     {
24         int p=last;
25         int np=newNode(step[p]+1);
26         while (p && !next[p][x]) next[p][x]=np,p=pre[p];
27         if (!p) pre[np]=1;
28         else
29         {
30             int q=next[p][x];
31             if (step[q]==step[p]+1) pre[np]=q;
32             else
33             {
34                 int nq=newNode(step[p]+1);
35                 for (int i=0;i<26;i++) next[nq][i]=next[q][i];
36                 pre[nq]=pre[q];
37                 pre[q]=pre[np]=nq;
38                 while (p&&next[p][x]==q) next[p][x]=nq,p=pre[p];
39             }
40
41         }
42         last=np;
43     }
44
45     inline void clear()
46     {
47         int tot=0;
48         last=newNode(tot);
49     }
50
51     inline int Query()
52     {
53         int ret=0,cnt=0;
54         int p=1;
55         for(int i=0;str[1][i];i++)
56         {
57             int index=str[1][i]-‘a‘;
58             if(next[p][index]) p=next[p][index],cnt++;
59             else
60             {
61                 while (p && !next[p][index]) p=pre[p];
62                 if(!p) p=1,cnt=0;
63                     else cnt=step[p]+1,p=next[p][index];
64                     /*由于沿着pre返回得到的字符串是当前字符串的后缀,所以第一个拥有index孩子的就是最长满足的后缀,长度即为step+1*/
65             }
66             ret=max(ret,cnt);
67         }
68         return ret;
69     }
70 }suf;
71
72 void init()
73 {
74     scanf("%d",&n);
75     scanf("%s",str[0]);
76     int len=strlen(str[0]);
77     suf.clear();
78     for (int i=0;i<len;i++) suf.extend(str[0][i]-‘a‘);
79     scanf("%s",str[1]);
80 }
81
82 int main()
83 {
84     init();
85     printf("%d",suf.Query());
86     return 0;
87 } 
时间: 2024-10-05 10:15:32

【SAM】codevs3160-最长公共子串的相关文章

[codevs3160]最长公共子串解题报告|后缀自动机

给出两个由小写字母组成的字符串,求它们的最长公共子串的长度. 样例就觉得不能更眼熟啊...好像之前用后缀数组做过一次 然后发现后缀自动机真的好好写啊...(当然当时学后缀数组的时候也这么认为... 这道题直接把第一个串放到后缀自动机里 第二个串在上面做匹配,但是要注意匹配的时候不能乱搞... 刚开始写了一个类似KMP的东西...想想不对啊 毕竟有些节点的深度是不对的 然而后来发现,我们可以用一个变量tem来保存当前的长度值 如果可以继续匹配,这个值就+1 否则就开始用fail指针不停地退,直到退

【文文殿下】后缀自动机(SAM)求最长公共子串的方法

首先,在A 串上建立一个SAM,然后用B串在上面跑.具体跑的方法是: 从根节点开始,建立一个指针 p ,指着B串的开头,同步移动指针,沿着SAM的边移动,如果可以移动(即存在边)那么万事皆好,直接len++就好,但是,如果无法继续转移(失配了),那么,我们考虑跳回其父节点,因为其父节点的Right集是当前状态的真超集,那么其父节点状态所代表的字符串的集合中的任意一个字符串,都是当前状态所代表的字符串集合中的正在匹配的字符串(会不会一定是最长串?)的后缀,所以,有一个贪心的思想:父节点状态中的最长

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

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

codevs 3160 最长公共子串(SAM)

3160 最长公共子串 题目描述 Description 给出两个由小写字母组成的字符串,求它们的最长公共子串的长度. 输入描述 Input Description 读入两个字符串 输出描述 Output Description 输出最长公共子串的长度 样例输入 Sample Input yeshowmuchiloveyoumydearmotherreallyicannotbelieveityeaphowmuchiloveyoumydearmother 样例输出 Sample Output 27

【Codevs3160】最长公共子串

当然先虐SAM裸题QwQ 3160 最长公共子串 时间限制: 2 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 给出两个由小写字母组成的字符串,求它们的最长公共子串的长度. 输入描述 Input Description 读入两个字符串 输出描述 Output Description 输出最长公共子串的长度 样例输入 Sample Input yeshowmuchiloveyoumydearmotherreallyicannotbeliev

【wikioi】3160 最长公共子串(后缀自动机)

http://codevs.cn/problem/3160/ sam的裸题...(之前写了spoj上另一题sam的题目,但是spoj被卡评测现在还没评测完QAQ打算写那题题解时再来详细介绍sam的....那就再等等吧. 求两个串的lcs话,就是先建立a串的sam,然后用b的字串去匹配a中. 因为sam中每个状态的len对应最长子串,因此自动机不断trans匹配时,如果没找到下一个点,那么在parent树的祖先中找是否还有子串可以更新(因为祖先的max比这个节点小,且都包含当前状态的right,所

求最长公共子串

poj2774,codevs3160 题目描述 Description 给出两个由小写字母组成的字符串,求它们的最长公共子串的长度. 输入描述 Input Description 读入两个字符串 输出描述 Output Description 输出最长公共子串的长度 样例输入 Sample Input yeshowmuchiloveyoumydearmotherreallyicannotbelieveityeaphowmuchiloveyoumydearmother 样例输出 Sample Ou

SPOJ 1811 Longest Common Substring(求两个串的最长公共子串)

http://www.spoj.com/problems/LCS/ 题目:求两个串的最长公共子串 分析: 以A建立SAM 让B在SAM上匹配可以类比于kmp思想,我们知道在Parent树上,fa是当前节点的子集,也就是说满足最大前缀,利用这个就可以做题了 #include <bits/stdc++.h> #define LL long long #define P pair<int, int> #define lowbit(x) (x & -x) #define mem(a

最长公共子串

(连续) - 阿里笔试[分析+编码] 题目描述:给定一个query和一个text,均由小写字母组成.要求在text中找出以同样的顺序连续出现在query中的最长连续字母序列的长度.例如,query为“acbac”,text为“acaccbabb”,那么text中的“cba”为最长的连续出现在query中的字母序列,因此,返回结果应该为其长度3.请注意程序效率. [思路]用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0.然后求出对角线最长的1序列,其对应的位置