SPOJ LCS (后缀自动机)
SPOJ LCS
Problem : 给两个串S、T,询问两个串的最长公共子串。
Solution :复(yu)习(xi)了一下后缀自动机。后缀自动机一定要结合后缀树来理解呀 !!!后缀自动机的fail指针实现了原串的逆序的后缀树,后缀自动机的nt转移数组相当于后缀树上的后缀链。
#include <iostream>
#include <string>
using namespace std;
const int N = 250008;
struct Suffix_Automaton
{
int nt[N << 1][26], fail[N << 1], a[N << 1];
int p, q, np, nq;
int tot, last, root;
int newnode()
{
for (int i = 0; i < 26; ++i) nt[tot][i] = -1;
fail[tot] = -1; a[tot] = 0;
return tot++;
}
void clear()
{
tot = last = 0;
root = newnode();
}
void insert(int ch)
{
p = last; last = np = newnode(); a[np] = a[p] + 1;
for (; ~p && nt[p][ch] == -1; p = fail[p]) nt[p][ch] = np;
if (p == -1) fail[np] = root;
else
{
q = nt[p][ch];
if (a[q] == a[p] + 1) fail[np] = q;
else
{
nq = newnode();
for (int i = 0; i < 26; ++i) nt[nq][i] = nt[q][i]; //i不要打成ch
fail[nq] = fail[q]; fail[q] = fail[np] = nq; //第一句 fail[q] 不一定等于p, nq是复制q的出边
a[nq] = a[p] + 1;
for (; ~p && nt[p][ch] == q; p = fail[p]) nt[p][ch] = nq;
}
}
}
void solve(const string &s)
{
int p = root, cnt = 0, ans = 0;
for (int i = 0, len = s.length(); i < len; ++i)
{
int ch = s[i] - 'a';
if (~nt[p][ch]) p = nt[p][ch], cnt++;
else
{
for(; ~p && nt[p][ch] == -1; p = fail[p]);
if (p == -1) p = root, cnt = 0;
else cnt = a[p] + 1, p = nt[p][ch];
}
ans = max(ans, cnt);
}
cout << ans << endl;
}
}sam;
int main()
{
cin.sync_with_stdio(0);
string s, t;
while (cin >> s >> t)
{
sam.clear();
for (int i = 0, len = s.length(); i < len; ++i)
sam.insert(s[i] - 'a');
sam.solve(t);
}
}