假設有兩個串分別爲S, T
求他們的最長公共子串
我們先對S建立後綴自動機, 然後從S的起點u開始, 再記一個長度L, 添加T[1 ~ LenT]
若存在子節點意味着添加當前字符後, 我們可以得到下一個狀態,此時令狀態u = next[u][c], L++.
若不存在, 即不斷跳lnk[u],直到有狀態v滿足next[v][c] != 0, 令u = next[v][c], L = len[v] + 1.
因爲lnk[u]代表着當前u的後綴, 這裏的匹配就與AC自動機的fail指針極其相似。
若一直到起點仍沒有則說明S中不含有c這個字符, 我們初始化u = 1, L = 0即可。
每次操作取個max即可得到答案。
這題後綴數組也能寫, 將兩個串用特殊字符拼接,然後二分長度,
檢測在一段連續的height內, 兩個串是否都出現了 。
不過sam比sa快好多了。。
後綴自動機:
後綴數組:
SAM:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <time.h>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#ifdef LOCAL
#define debug(x) cout << "[" __FUNCTION__ ": " #x " = " << (x) << "]\n"
#define TIME cout << "RuningTime: " << clock() << "ms\n", 0
#else
#define TIME 0
#endif
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
char a[N];
char b[N];
int ans;
struct Suffix_Automata
{
int len[N << 1];
int lnk[N << 1];
int cnt[N << 1];
int sub[N << 1];
int nxt[N << 1][26];
int idx;
int last;
void init()
{
last = idx = 1;
lnk[1] = len[1] = 0;
}
void clear()
{
memset(len, 0, sizeof len);
memset(lnk, 0, sizeof lnk);
memset(cnt, 0, sizeof cnt);
memset(nxt, 0, sizeof nxt);
}
void extend(int c)
{
int x = ++idx;
len[x] = len[last] + 1;
sub[x] = 1;
int p;
for (p = last; p && !nxt[p][c]; p = lnk[p])
nxt[p][c] = x;
if (!p)
lnk[x] = 1, cnt[1]++;
else
{
int q = nxt[p][c];
if (len[p] + 1 == len[q])
lnk[x] = q, cnt[q]++;
else
{
int nq = ++idx;
len[nq] = len[p] + 1;
lnk[nq] = lnk[q];
memcpy(nxt[nq], nxt[q], sizeof nxt[q]);
for (; p && nxt[p][c] == q; p = lnk[p])
nxt[p][c] = nq;
lnk[q] = lnk[x] = nq;
cnt[nq] += 2;
}
}
last = x;
}
}sam;
void solve()
{
int len2 = strlen(b);
int u = 1, L = 0;
for (int i = 0; i < len2; i++)
{
int id = b[i] - 'a';
while (!sam.nxt[u][id] && u != 1)
u = sam.lnk[u], L = sam.len[u];
if (sam.nxt[u][id] != 0)
u = sam.nxt[u][id], ++L;
ans = max(ans, L);
}
cout << ans << endl;
}
int main()
{
#ifdef LOCAL
freopen("D:/input.txt", "r", stdin);
#endif
scanf("%s", a);
int len = strlen(a);
sam.init();
for (int i = 0; i < len; i++)
sam.extend(a[i] - 'a');
scanf("%s", b);
solve();
return TIME;
}
SA
#include <iostream>
//#include <unordered_map>
#include <time.h>
#include <algorithm>
#include <stdio.h>
#include <string.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#ifdef LOCAL
#define debug(x) cout << "[" __FUNCTION__ ": " #x " = " << (x) << "]\n"
#define TIME cout << "RuningTime: " << clock() << "ms\n", 0
#else
#define TIME 0
#endif
#define hash_ 1000000009
#define Continue(x) { x; continue; }
#define Break(x) { x; break; }
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
#define gc p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++;
inline int read(){ static char buf[1000000], *p1 = buf, *p2 = buf; register int x = false; register char ch = gc; register bool sgn = false; while (ch != '-' && (ch < '0' || ch > '9')) ch = gc; if (ch == '-') sgn = true, ch = gc; while (ch >= '0'&& ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gc; return sgn ? -x : x; }
ll fpow(ll a, int b, int mod) { ll res = 1; for (; b > 0; b >>= 1) { if (b & 1) res = res * a % mod; a = a * a % mod; } return res; }
int MX;
char str[N];
char str1[N];
char str2[N];
struct Suffix_Array
{
int n, r;
int sa[N];
int cnt[N];
int rak[N];
int tmp[N];
int heig[N];
void radix_sort(int *rk, int *tp)
{
memset(cnt, 0, sizeof cnt);
for (int i = 1; i <= n; i++)
cnt[rk[tp[i]]]++;
for (int i = 1; i <= r; i++)
cnt[i] += cnt[i - 1];
for (int i = n; i >= 1; i--)
sa[cnt[rk[tp[i]]]--] = tp[i];
}
void suffix()
{
int *rk = rak, *tp = tmp;
for (int i = 1; i <= n; i++)
rk[i] = str[i], tp[i] = i;
r = 127; // 0 ~ 127
radix_sort(rk, tp);
for (int l = 1, p = 1, i; p < n; l <<= 1, r = p)
{
for (p = 0, i = n - l + 1; i <= n; i++)
tp[++p] = i;
for (i = 1; i <= n; i++)
if (sa[i] > l)
p++, tp[p] = sa[i] - l;
radix_sort(rk, tp);
swap(rk, tp);
rk[sa[1]] = p = 1;
for (i = 2; i <= n; i++)
{
if (tp[sa[i]] != tp[sa[i - 1]] || tp[sa[i] + l] != tp[sa[i - 1] + l])
p++;
rk[sa[i]] = p;
}
}
}
void get_height()
{
for (int i = 1; i <= n; i++)
rak[sa[i]] = i;
int k = 0;
for (int i = 1; i <= n; i++)
{
if (k)
k--;
int j = sa[rak[i] - 1];
while (str[i + k] == str[j + k])
k++;
heig[rak[i]] = k;
}
}
}sa;
int main()
{
#ifdef LOCAL
freopen("D:/input.txt", "r", stdin);
#endif
int t;
cin >> t;
int cnt = 0;
scanf("%s", str1 + 1);
int len = strlen(str1 + 1);
for (int i = 1; i <= len; i++)
str[++cnt] = str1[i];
str[++cnt] = 1;
scanf("%s", str2 + 1);
int len2 = strlen(str2 + 1);
for (int i = 1; i <= len2; i++)
str[++cnt] = str2[i];
sa.n = cnt;
sa.suffix();
sa.get_height();
int ans = 0;
for (int i = 2; i <= sa.n; i++)
{
if (sa.heig[i] > ans && (sa.sa[i - 1] <= len && sa.sa[i] > len || sa.sa[i - 1] > len && sa.sa[i] <= len))
ans = sa.heig[i];
}
cout << ans << endl;
return TIME;
}