[NOI Online 2021 提高组] 积木小赛

思路不说了。
想起来自己打比赛的时候,没睡好。随便写了个\(HASH\),模数开小一半分都没有。
然后学了\(SAM\),发现这个判重不就是个水题。
\(SAM\)是字串tire的集合体。
随便\(dfs\)一下就好,然后复杂度是\(O(n^2)\)即遍历所有子串

[NOI Online 2021 提高组] 积木小赛
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 3005
#define end 0x3f3f3f3f

ll n;
char a[N],b[N];
ll nex[N][30];

ll ch[N << 1][30],link[N << 1],len[N << 1],nod = 1,las = 1;//SAM

inline void insert(ll c){
	ll p = las,q = ++nod;las = q;
	len[q] = len[p] + 1;
	while(!ch[p][c] && p != 0){
		ch[p][c] = q;
		p = link[p];
	}
	if(p == 0)
	link[q] = 1;
	else{
		ll x = ch[p][c];
		if(len[x] == len[p] + 1){
			link[q] = x;
		}else{
			int y = ++ nod ;//复制一个新节点
			link[y] = link[x];
			link[x] = link[q] = y;
			len[y] = len[p] + 1;
			std::memcpy(ch[y],ch[x],sizeof(ch[x])); 
			while(p != 0 && ch[p][c] == x){
				ch[p][c] = y;
				p = link[p];
			}
		}
	}
}

ll ans;

inline void dfs(int u,int to,ll now){
	if(to == end)
	return;
	ans ++ ;
	for(int i = 0;i <= 26;++i){
		if(ch[u][i])
		dfs(ch[u][i],nex[to + 1][i],now * 10 + i);
	}
}

int main(){
	scanf("%lld",&n);
	scanf("%s%s",a + 1,b + 1);
	for(int i = 0;i <= 26;++i)
	nex[n + 1][i] = end;
	for(int i = n;i >= 1;--i){
	for(int j = 0;j <= 26;++j)
	nex[i][j] = nex[i + 1][j];
	nex[i][a[i] - 'a'] = i;
	}
	for(int i = 0;i <= 26;++i)
	nex[0][i] = nex[1][i];
	for(int i = 1;i <= n;++i)
	insert(b[i] - 'a');
	dfs(1,0,0);
	std::cout<<ans - 1<<std::endl;
}
posted @ 2021-06-17 20:20  fhq_treap  阅读(70)  评论(0编辑  收藏  举报