模板题。
给出两个串S, T, 求最长公共子串。|S|, |T| <= 1e5. 常规nlogn,后缀自动机O(n).
后缀自动机看了1天多了,还是晕半死,难以理解的构造原理,看了半天终于大概懂了如何构造的,嗯,拍了个模板,用一个字符串建立后缀自动机。那么…建好了之后能干嘛呢?一片空白。。又看了半天终于才懂了如何求LCS的。总结就是对A建后缀自动机,然后用B去匹配,若能匹配上就转移到儿子,否则沿着parent树向上跳。
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
const int maxn = (250000+5)<<1;
string s;
struct SAM{
int son[maxn][26], pre[maxn], step[maxn],last, tot;
void init(){
last = tot = 0;
memset(son[0], -1, sizeof son[0]);
pre[0] = -1; step[0] = 0;
}
void extend(int c){
int p = last, np = ++tot;
step[np] = step[p] + 1;
memset(son[np], -1, sizeof son[np]);
while(p!=-1 && son[p][c] == -1) son[p][c] = np, p = pre[p];
if(p == -1) pre[np] = 0;
else{
int q = son[p][c];
if(step[q] != step[p] + 1){
int nq = ++tot;
memcpy(son[nq], son[q], sizeof son[q]);
step[nq] = step[p] + 1;
pre[nq] = pre[q];
pre[q] = pre[np] = nq;
while(p!=-1 && son[p][c] == q) son[p][c] = nq, p = pre[p];
}
else pre[np] = q;
}
last = np;
}
void build(string s)
{
init();
for(int i = 0; i < s.size(); i++) extend(s[i] - 'a');
}
int lcs_find(const string &s)
{
int sz = s.size(), u = 0, tmp = 0, c, res = 0;
for(int i = 0; i < sz; i++)
{
c = s[i] - 'a';
if(son[u][c]!=-1) tmp++, u = son[u][c];
else
{
while(u!=-1 && son[u][c] == -1) u = pre[u];
if(u!=-1) tmp = step[u] + 1, u = son[u][c];
else tmp = 0, u = 0;
}
res = max(res, tmp);
}
return res;
}
void debug()
{
for(int i = 0; i <= tot; ++i)
{
printf("id=%d, fa=%d, step=%d, son=[ ", i, pre[i], step[i]);
for(int j = 0; j < 26; ++j)
{
if(son[i][j]!=-1)
printf("%c,%d ", j+'a',son[i][j]);
}
puts("]");
}
}
} sam;
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
cin>>s;
sam.build(s);
// sam.debug();
cin >> s;
cout << sam.lcs_find(s) << endl;
return 0;
}