题目链接:https://cn.vjudge.net/problem/SPOJ-LCS
题意:s, t 两串的最长公共子串
题解:s建树,枚举 t ,失配后通过回溯fa[],来继续进行
后缀自动机:https://blog.youkuaiyun.com/mmk27_word/article/details/98475090
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 250010;
int X[N << 1], Y[N << 1];
struct SAM {
int n; // 字符串长度
int tot; // 节点编号
int len[N << 1]; // 最长子串的长度 (该节点子串数量 = len[x] - len[fa[x]])
int fa[N << 1]; // // 后缀链接 (最短串前部少一个字符所到达的状态)
int cnt[N << 1]; // 被后缀链接的个数,方便求节点字符串的个数
int nex[N << 1][26]; // 下一个状态
int last; // 最后一个节点
ll num[N << 1]; // 该状态子串的数量
ll maxx[N << 1]; // 长度为x的子串出现次数最多的子串的数目
ll sum[N << 1]; // 该节点后面所形成的自字符串的总数
ll ans; // 不同字符串数目
ll sublen; // 不同字符串总长度
void init(int x) {
for(int i = 0; i <= x * 2 + 10; i++) {
maxx[i] = 0;
cnt[i] = 0;
num[i] = 0;
for(int j = 0; j <= 25; j++)
nex[i][j] = 0;
}
n = 0;
last = 1;
tot = 1; // 1是起始点 空集
fa[1] = 0;
len[1] = 0;
}
void insert (int c) {
n++;
int x = ++tot; // 新建节点
len[x] = len[last] + 1;
num[x] = 1;
int p;
for(p = last; p && !nex[p][c]; p = fa[p]) nex[p][c] = x; //沿着后缀连接 将所有没有字符c转移的节点直接指向新节点
if(!p) fa[x] = 1, cnt[1]++; // 没有c的转移,直接链接到起点
else {
int q = nex[p][c];
if(len[p] + 1 == len[q]) // p q连续
fa[x] = q, cnt[q]++;
else {
int nq = ++tot; // 不连续 复制一份q的 使得和p连续
len[nq] = len[p] + 1;
fa[nq] = fa[q];
memcpy(nex[nq], nex[q], sizeof(nex[q]));
for(; p && nex[p][c] == q; p = fa[p]) nex[p][c] = nq; //沿着后缀连接 将所有通过c转移为q的改为nq
fa[q] = fa[x] = nq;
cnt[nq] += 2;
}
}
last = x;
}
void getposnum() { // 每个节点子串出现的次数
queue<int> q;
int x;
for(int i = 1; i <= tot; i++)
if(!cnt[i])
q.push(i);
while(!q.empty()) {
x = q.front(); q.pop();
num[fa[x]] += num[x];
if(--cnt[fa[x]] == 0) q.push(fa[x]);
}
}
void getSUM() {
for(int i = tot; i >= 1; i--) {
sum[i] = num[i];
for(int j = 0; j <= 25; j++)
sum[i] += sum[nex[i][j]];
// cout << i << " " << sum[i] << endl;
}
}
void getsubnum() { // 不同字符串数目
ans = 0;
for(int i = 1; i <= tot; i++)
ans += len[i] - len[fa[i]];
}
void getmaxx() { // 长度为x的子串出现次数最多的子串的数目
// getposnum();
for(int i = 1; i <= tot; i++) maxx[len[i]] = max(maxx[len[i]], num[i]);
for(int i = n - 1; i >= 1; i--) maxx[i] = max(maxx[i], maxx[i + 1]);
}
/*这也是计算节点子串出现次数的方法
void count() {
memset(X,0,sizeof(X));
for(int i=1;i<=tot;i++) X[len[i]]++;
for(int i=1;i<=tot;i++) X[i]+=X[i-1];
for(int i=1;i<=tot;i++) Y[X[len[i]]--]=i;
for(int i=tot;i>=1;i--) num[fa[Y[i]]]+=num[Y[i]];
for(int i = tot;i >= 1; i--) {
sum[Y[i]] = 1;
for(int j = 0 ;j < 25; j++)
sum[Y[i]] += ans[nex[Y[i]][j]];
}
}
*/
void getsublen() {
sublen = 0;
for(int i = 1; i <= tot; i++) {
sublen += 1LL * (len[i] + len[fa[i]] + 1) * (len[i] - len[fa[i]]) / 2;
}
// cout << sublen << endl;
}
int compare(char str[]) {
int l = strlen(str);
int res = 0, cnt = 0;
for(int i = 0, p = 1; i < l; i++) {
if(nex[p][str[i] - 'a']) cnt++, p = nex[p][str[i] - 'a'];
else {
while(p && !nex[p][str[i] - 'a']) p = fa[p];
if(!p) cnt = 0, p = 1;
else cnt = len[p] + 1, p = nex[p][str[i] - 'a'];
}
res = max(res, cnt);
}
// printf("%d\n", res);
return res;
}
}sam;
char s[N], t[N];
int k;
int main() {
int T, len;
ll ans;
while(~scanf("%s", s + 1)) {
scanf("%s", t + 1);
len = strlen(s + 1);
sam.init(len);
for(int i = 1; i <= len; i++)
sam.insert(s[i] - 'a');
printf("%d\n", sam.compare(t + 1));
}
return 0;
}