后缀自动机 ---- P3804 【模板】后缀自动机(求每个等价类最长串的出现次数)

后缀自动机一些关键点

  1. 首先后缀自动机上面每个节点都是一个等价类并且是最长的字符串的结尾
  2. 后缀自动机上的fail链反建就是parent tree,下面是SAM和Parent tree的构造
  3. 后缀自动机 ---- P3804 【模板】后缀自动机(求每个等价类最长串的出现次数)_第1张图片

后缀自动机 ---- P3804 【模板】后缀自动机(求每个等价类最长串的出现次数)_第2张图片

对于这道模板题我们先看看代码:

  1. 这里有一个关键点就是我们设 n u m [ i ] num[i] num[i]为SAM中这个点是否为终止节点。我们根据 p a r e n t    t r e e parent\;tree parenttree的定义,知道非终止节点的是会被下面全部终止节点所覆盖的!!
  2. 因为我们根据SAM的构建我们知道之所以要分裂出一个非终止节点是因为之前出了和我们一样的后缀并且结尾和当前点一样,要把这条链复制过来,怎么复制?就是新开一个非终止节点并且链接上当前要插入的终止节点就相当于把所有的和当前节点形成的后缀都插入了!!,那么这个非终止节点就被终止节点覆盖了!
  3. 这里还是要理解:就是假如现在我们要插入 a [ i ] a[i] a[i],那么SAM里面已经有了 a [ 0 ] , a [ 1 ] . . . . a [ i − 2 ] , a [ i − 1 ] a[0],a[1]....a[i-2],a[i-1] a[0],a[1]....a[i2],a[i1]组成的所有的后缀了!!现在要的新后缀就是 a [ 0... i ] , a [ 1... i ] , a [ 2.. i ] . . . a [ i − 1 , i ] , a [ i ] a[0...i],a[1...i],a[2..i]...a[i-1,i],a[i] a[0...i],a[1...i],a[2..i]...a[i1,i],a[i] i + 1 i+1 i+1个新的后缀
#include 
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = N<<2;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}
struct SAM {
	int ch[30];
	int fa, len;
}node[maxn];
int tot = 1, las = 1;
vector<int> G[maxn];
ll ans;
ll num[maxn];
// p 是上次最后的点,np是当前点
void add(int c) {
	int p = las, np = ++ tot; 
	num[tot] = 1; // 注意我们这里是对SAM里面的终止节点进行设置成1
	las = np;
	node[np].len = node[p].len + 1;
	for(;p&&!node[p].ch[c]; p = node[p].fa) node[p].ch[c] = np;
	if(!p) node[np].fa = 1;
	else {
		int v = node[p].ch[c];
		if(node[v].len == node[p].len + 1) node[np].fa = v;
		else {
		   int nv = ++ tot;
		   node[nv] = node[v];
		   node[nv].len = node[p].len + 1;
		   for(;p&&node[p].ch[c] == v; p = node[p].fa) node[p].ch[c] = nv;
		   node[np].fa = node[v].fa = nv;
		}
	}
}

inline void dfs(int u) {
	for(auto it : G[u]) {
		dfs(it);
		num[u] += num[it]; 
	}
	if(num[u] != 1) ans = max(ans,1ll*num[u]*node[u].len);
}

int main() {
	IOS;
	string s;
	cin >> s;
	for(int i = 0; i < s.length(); ++ i) 
	  add(s[i]-'a');
	for(int i = 2; i <= tot; ++ i)
	   G[node[i].fa].push_back(i);
	dfs(1);
	cout << ans;
}

你可能感兴趣的:(#,后缀自动机,node.js,python)