【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 } 

 

转载于:https://www.cnblogs.com/iiyiyi/p/5743687.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值