http://www.spoj.com/problems/LCS/
题目:求两个串的最长公共子串
参考:https://www.cnblogs.com/autoint/p/10345276.html:
分析:
给定两个字符串 S 和 T ,求出最长公共子串,公共子串定义为在 S 和 T 中 都作为子串出现过的字符串 X 。
我们为字符串 S 构造后缀自动机。
我们现在处理字符串 T ,对于每一个前缀都在 S 中寻找这个前缀的最长后缀。换句话 说,对于每个字符串 T 中的位置,我们想要找到这个位置结束的 S 和 T 的最长公 共子串的长度。
为了达到这一目的,我们使用两个变量,当前状态 v 和 当前长度 l 。这两 个变量描述当前匹配的部分:它的长度和它们对应的状态。
一开始 v=t_0且 l=0 ,即,匹配为空串。
现在我们来描述如何添加一个字符 T[i] 并为其重新计算答案:
如果存在一个从 v 到字符 T[i] 的转移,我们只需要转移并让 l 自增一。
如果不存在这样的转移,我们需要缩短当前匹配的部分,这意味着我们需要按照以下后 缀链接进行转移:
v=link(v)
与此同时,需要缩短当前长度。显然我们需要将 l 赋值为 len(v) ,因为经过这个后缀链接后我们到达的状态所对应的最长字符串是一个子串。
如果仍然没有使用这一字符的转移,我们继续重复经过后缀链接并减小 l ,直到我们 找到一个转移或到达虚拟状态 -1 (这意味着字符 T[i] 根本没有在 S 中出现过, 所以我们设置 v=l=0 )。
问题的答案就是所有 l 的最大值。
这一部分的时间复杂度为 O(length(T)) ,因为每次移动我们要么可以使 l 增加一, 要么可以在后缀链接间移动几次,每次都减小 l 的值。
时间复杂度O(|S|+|T|)
#include <bits/stdc++.h> #define LL long long #define P pair<int, int> #define lowbit(x) (x & -x) #define mem(a, b) memset(a, b, sizeof(a)) #define rep(i, a, n) for (int i = a; i <= n; ++i) const int maxn =1000005; #define mid ((l + r) >> 1) #define lc rt<<1 #define rc rt<<1|1 using namespace std; string str1,str2; struct SAM{ int trans[maxn<<1][26], slink[maxn<<1], maxlen[maxn<<1]; int last, now, root, len; inline void newnode (int v) { maxlen[++now] = v; } inline void extend(int c) { newnode(maxlen[last] + 1); int p = last, np = now; // 更新trans while (p && !trans[p][c]) { trans[p][c] = np; p = slink[p]; } if (!p) slink[np] = root; else { int q = trans[p][c]; if (maxlen[p] + 1 != maxlen[q]) { // 将q点拆出nq,使得maxlen[p] + 1 == maxlen[q] newnode(maxlen[p] + 1); int nq = now; memcpy(trans[nq], trans[q], sizeof(trans[q])); slink[nq] = slink[q]; slink[q] = slink[np] = nq; while (p!=-1 && trans[p][c] == q) { trans[p][c] = nq; p = slink[p]; } }else slink[np] = q; } last = np; // 初始状态为可接受状态 } inline void init() { memset(trans,0,sizeof(trans)); memset(slink,0,sizeof(slink)); memset(maxlen,0,sizeof(maxlen)); root = last=now=1; } inline void build(string s) { len=s.size(); for(int i=0 ; i<len ; i++) extend(s[i]-'a'); } inline int work(string s) { int Len=s.size(); int t1=0; int ret=0; int now=root; for(int i=0 ; i<Len ; i++) { int ind=s[i]-'a'; while(now!=0 && trans[now][ind]==0) { now=slink[now]; if(now!=0) t1=maxlen[now]; } if(now==0) { now=root ; t1=0; } else { now=trans[now][ind]; t1++; ret=max(ret,t1); } } return ret; } }sam; int main() { sam.init(); cin>>str1>>str2; sam.build(str1); printf("%d\n",sam.work(str2)); }
时间复杂的O(n)