【BZOJ4566】[HAOI2016]找相同字符
阿新 • • 發佈:2019-01-22
tdi 字符串 return sin break line -- 有一個 n)
【BZOJ4566】[HAOI2016]找相同字符
題面
給定兩個字符串,求出在兩個字符串中各取出一個子串使得這兩個子串相同的方案數。兩個方案不同當且僅當這兩個子串中有一個位置不同。
其中\(1\leq|s1|,|s2|\leq n\)
題解
其實和這題差不多。
根據後綴數組常用套路,將將\(s1,s2\)用一個未曾出現的字符連起來
和上面那題一樣的方法
算出來一個答案
然後減去分別左右兩字符串選的貢獻就好啦
代碼
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int MAX_N = 4e5 + 5; int N; char a[MAX_N], b[MAX_N], c[MAX_N]; int sa[MAX_N], rnk[MAX_N], lcp[MAX_N]; void GetSA() { #define cmp(i, j, k) (y[i] == y[j] && y[i + k] == y[j + k]) static int x[MAX_N], y[MAX_N], bln[MAX_N]; int M = 122; for (int i = 0; i <= M; i++) bln[i] = 0; for (int i = 1; i <= N; i++) bln[x[i] = a[i]]++; for (int i = 1; i <= M; i++) bln[i] += bln[i - 1]; for (int i = N; i >= 1; i--) sa[bln[x[i]]--] = i; for (int k = 1; k <= N; k <<= 1) { int p = 0; for (int i = 0; i <= M; i++) y[i] = 0; for (int i = N - k + 1; i <= N; i++) y[++p] = i; for (int i = 1; i <= N; i++) if (sa[i] > k) y[++p] = sa[i] - k; for (int i = 0; i <= M; i++) bln[i] = 0; for (int i = 1; i <= N; i++) bln[x[y[i]]]++; for (int i = 1; i <= M; i++) bln[i] += bln[i - 1]; for (int i = N; i >= 1; i--) sa[bln[x[y[i]]]--] = y[i]; swap(x, y); x[sa[1]] = p = 1; for (int i = 2; i <= N; i++) x[sa[i]] = cmp(sa[i], sa[i - 1], k) ? p : ++p; if (p >= N) break; M = p; } } void GetLcp() { for (int i = 1; i <= N; i++) rnk[sa[i]] = i; for (int i = 1, j = 0; i <= N; i++) { if (j) --j; while (a[i + j] == a[sa[rnk[i] - 1] + j]) ++j; lcp[rnk[i]] = j; } } typedef long long ll; int lp[MAX_N], rp[MAX_N], stk[MAX_N], top; ll solve() { GetSA(), GetLcp(); top = 0, stk[0] = 1; for (int i = 2; i <= N; i++) { while (top > 0 && lcp[stk[top]] >= lcp[i]) --top; lp[i] = i - stk[top], stk[++top] = i; } top = 0, stk[0] = N + 1; for (int i = N; i >= 2; i--) { while (top > 0 && lcp[stk[top]] > lcp[i]) --top; rp[i] = stk[top] - i, stk[++top] = i; } ll res = 0; for (int i = 2; i <= N; i++) res += 1ll * lp[i] * rp[i] * lcp[i]; return res; } int main () { ll ans = 0; int n, m; scanf("%s", b + 1); scanf("%s", c + 1); n = strlen(b + 1); m = strlen(c + 1); N = n; for (int i = 1; i <= N; i++) a[i] = b[i]; ans -= solve(); N = m; for (int i = 1; i <= N; i++) a[i] = c[i]; ans -= solve(); N = n + m + 1; for (int i = 1; i <= n; i++) a[i] = b[i]; a[n + 1] = '#'; for (int i = 1; i <= m; i++) a[i + n + 1] = c[i]; ans += solve(); printf("%lld\n", ans); return 0; }
【BZOJ4566】[HAOI2016]找相同字符