题意:
给出一个长度为n的串s,每次询问给出两个整数集合:S,T,求 ∑ x ∈ S ∑ y ∈ T L C P ( S [ x , n ] , S [ y , n ] ) \sum_{x \in S} \sum_{y \in T}LCP(S[x,n],S[y,n]) ∑x∈S∑y∈TLCP(S[x,n],S[y,n])
题解:
先将S reverse一下,询问等价于 前缀的最长公共后缀,而两个串的最长公共后缀,对应于他们sam节点的fail树上lca点的len。
因此先构造出sam,然后将fail树链剖搞好(留着求lca用),每个点的权值是sam节点表示最长串的len,然后对于询问,建虚树dp一下。
套板子很简单的。
所以说,板子一定还是要大力封装才方便用。
Code:
//
// Created by calabash_boy on 18-6-4.
//SPOJ substring
// calc ans_i=长度=i的所有子串,出现次数最多的一种出现了多少次。
//
//
// Created by calabash_boy on 18-10-18.
//
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 4e5+100;
char s[maxn];
int n;
int first[maxn],des[maxn*2],nxt[maxn*2],tot;
int a[maxn];
inline void addEdge(int x,int y){
tot ++;
des[tot] = y;
nxt[tot] = first[x];
first[x] = tot;
}
/*注意需要按l将节点基数排序来拓扑更新parent树*/
struct Suffix_Automaton{
//basic
int nxt[maxn*2][26],fa[maxn*2],l[maxn*2];
int last,cnt;
//special
int end_pos[maxn*2];
void clear(){
last =cnt=1;
fa[1]=l[1]=0;
memset(nxt[1],0,sizeof nxt[1]);
}
void init(char *s){
while (*s){
add(*s-'a');
s++;
}
}
void add(int c){
int p = last;
int np = ++cnt;
memset(nxt[cnt],0,sizeof nxt[cnt]);
l[np] = l[p]+1;
last