[传送门]
直接想最暴力的做法就是正解了。
每次询问都把两个串的回文树建出来,然后再两棵树上同时dfs,经过相同的节点答案就加一。遇到一个不存在的就退出。
再把询问记忆化一下就OK了。
复杂度是 $O(n \sqrt n)$
因为每次dfs的复杂度跟短的串的长度有关。
自己写给写T了。学习了下优秀写法。多开一个数组表示next是不是当前的next的。这样就不用每次都清数组了。
#include <bits/stdc++.h> #define pii pair<int, int> #define fi first #define se second using namespace std; const int N = 1e5 + 7; const int sz = 26; struct PAM { int s[N], fail[N], len[N]; pii ne[N][sz]; int n, tol, last, cnt; void init() { n = last = 0; tol = 1; s[0] = len[1] = -1; fail[0] = 1; ++cnt; } inline int getid(int x) { while (s[n - len[x] - 1] != s[n]) x = fail[x]; return x; } inline void extend(int c) { s[++n] = c; int cur = getid(last); if (ne[cur][c].se != cnt) { int k = getid(fail[cur]); fail[++tol] = ne[k][c].se == cnt ? ne[k][c].fi : 0; ne[cur][c] = pii(tol, cnt); len[tol] = len[cur] + 2; } last = ne[cur][c].fi; } } pam[2]; vector<char> s[N]; map<pii, int> mp; char str[N]; int ans; void dfs(int u, int v) { for (int i = 0; i < sz; i++) { if (pam[0].ne[u][i].se == pam[0].cnt && pam[1].ne[v][i].se == pam[1].cnt) { ans++; dfs(pam[0].ne[u][i].fi, pam[1].ne[v][i].fi); } } } int main() { //freopen("in.txt", "r", stdin); //freopen("out.txt", "w", stdout); int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%s", str); int len = strlen(str); for (int j = 0; j < len; j++) s[i].push_back(str[j]); } int q; scanf("%d", &q); while (q--) { int a, b; scanf("%d%d", &a, &b); a ^= ans, b ^= ans; if (s[a].size() > s[b].size()) swap(a, b); if (mp.count(pii(a, b))) { printf("%d\n", mp[pii(a, b)]); continue; } pam[0].init(), pam[1].init(); int len = s[a].size(); for (int i = 0; i < len; i++) pam[0].extend(s[a][i] - 'a'); len = s[b].size(); for (int i = 0; i < len; i++) pam[1].extend(s[b][i] - 'a'); ans = 0; dfs(0, 0); dfs(1, 1); printf("%d\n", mp[pii(a, b)] = ans); } return 0; }