1. 程式人生 > >[洛谷P3181][HAOI2016]找相同字元

[洛谷P3181][HAOI2016]找相同字元

題目大意:給你兩個字串,求從兩個字串中各選擇一個字串使得這兩個字串相同的方案數。

題解:建廣義$SAM$,對每個點求出在第一個串中出現次數和第二個串中出現次數,乘起來就行了

卡點:

 

C++ Code:

#include <algorithm>
#include <cstdio>
#include <cstring>
#define maxn 200010

namespace SAM {
#define N (maxn << 2)
	int R[N], nxt[N][26], fail[N];
	int lst = 1, idx = 1, sz[N][2];
	void append(char __ch, int op) {
		int ch = __ch - 'a';
		int p = lst, np = lst = ++idx; R[np] = R[p] + 1; sz[np][op] = 1;
		for (; p && !nxt[p][ch]; p = fail[p]) nxt[p][ch] = np;
		if (!p) fail[np] = 1;
		else {
			int q = nxt[p][ch];
			if (R[q] + 1 == R[p]) fail[np] = q;
			else {
				int nq = ++idx;
				fail[nq] = fail[q], R[nq] = R[p] + 1, fail[q] = fail[np] = nq;
				std::copy(nxt[q], nxt[q] + 26, nxt[nq]);
				for (; p && nxt[p][ch] == q; p = fail[p]) nxt[p][ch] = nq;
			}
		}
	}

	int head[N], cnt;
	struct Edge {
		int to, nxt;
	} e[N];
	inline void addedge(int a, int b) {
		e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
	}

	long long ans;
	void dfs(int u) {
		for (int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			dfs(v);
			sz[u][0] += sz[v][0], sz[u][1] += sz[v][1];
			ans += static_cast<long long> (R[v] - R[u]) * sz[v][0] * sz[v][1];
		}
	}
	long long work() {
		for (int i = 2; i <= idx; i++) addedge(fail[i], i);
		dfs(1);
		return ans;
	}
#undef N
}

int n;
char s[maxn];
int main() {
	scanf("%s", s); n = strlen(s);
	for (int i = 0; i < n; i++) SAM::append(s[i], 0);
	scanf("%s", s); n = strlen(s); SAM::lst = 1;
	for (int i = 0; i < n; i++) SAM::append(s[i], 1);
	printf("%lld\n", SAM::work());
	return 0;
}