克拉克是一名人格分裂患者.有一天克拉克分裂成 n n 个人.
每个克拉克手里有一个由小写字母组成字符串 ai a i .克拉克们还有 q q 次询问,第 i i 次询问,克拉克们想知道有多少个回文串同时出现在 axi a x i 和 ayi a y i 中.
一个字符串称为回文串当且仅当这个串前后反转后与这个串相同。
n,q,∑ai⩽100000 n , q , ∑ a i ⩽ 100000
对于每组询问,暴力构出两个串的回文树,然后在较短的串中查询即可。为了加快速度,为每组询问加上记忆化。
然后就AC啦?!
让我们证明一下这样做的正确性,即时间复杂度为 O(nn−−√) O ( n n ) :
设每组询问的串为 x x , y y
设 |sx|⩽n−−√ | s x | ⩽ n 或 |sy|⩽n−−√ | s y | ⩽ n ,由于时间复杂度与较短的串有关,所以复杂度显然正确。
否则 |sx|⩾n−−√ | s x | ⩾ n 的串最多有 n−−√ n 个, |sx|⩾n−−√ | s x | ⩾ n 且 |sy|⩾n−−√ | s y | ⩾ n 最多只有 n n 种询问,加上记忆化后显然正确。
#include
using namespace std;
const int maxn = 100005;
int n, q;
vector<char> s[maxn];
inline int gi()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
int sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
mapint , int>, int> f;
struct palindromic_tree
{
pair<int, int> ch[maxn][26];
int cnt, tot, ans, len[maxn], fail[maxn], last;
inline void clear()
{
last = 1; len[tot = 1] = -1; fail[0] = fail[1] = 1; ++cnt;
}
inline void add(int t, int c, int n)
{
int x = last;
while(s[t][n - len[x] - 1] != s[t][n]) x = fail[x];
if(ch[x][c].second != cnt) {
int v = ++tot, k = fail[x];
while(s[t][n - len[k] - 1] != s[t][n]) k = fail[k];
fail[v] = ch[k][c].second == cnt ? ch[k][c].first : 0; len[v] = len[x] + 2; ch[x][c] = make_pair(v, cnt);
}
last = ch[x][c].first;
}
}t1, t2;
int l, r, que1[maxn], que2[maxn];
inline int bfs(int s)
{
l = 0; r = 1; que1[r] = que2[r] = s;
do {
++l; int u = que1[l], v = que2[l];
for(int i = 0; i < 26; ++i)
if(t1.ch[u][i].second == t1.cnt && t2.ch[v][i].second == t2.cnt) {
++r; que1[r] = t1.ch[u][i].first; que2[r] = t2.ch[v][i].first;
}
}while(l < r);
return r - 1;
}
inline int solve(int x, int y)
{
if(f.count(make_pair(x, y))) return f[make_pair(x, y)];
if(s[x].size() > s[y].size()) swap(x, y);
t1.clear(); t2.clear();
register int n, i;
for(n = s[x].size(), i = 0; i < n; ++i) t1.add(x, s[x][i] - 'a', i);
for(n = s[y].size(), i = 0; i < n; ++i) t2.add(y, s[y][i] - 'a', i);
return f[make_pair(x, y)] = bfs(0) + bfs(1);
}
int main()
{
scanf("%d\n", &n);
for(int i = 1; i <= n; ++i) {
char c = getchar(); s[i].push_back(0);
while('a' <= c && c <= 'z') s[i].push_back(c), c = getchar();
}
scanf("%d\n", &q);
for(int lastans = 0, i = 1, x, y; i <= q; ++i) {
x = gi() ^ lastans; y = gi() ^ lastans;
printf("%d\n", lastans = solve(x, y));
}
return 0;
}