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

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

题目大意:给定串\(A\),\(B\),求\(B\)中有多少本质不同的连续子段是\(A\)的子序列

\(n\leq 3000\)

暴力枚举\(B\)中的子段,同步维护与\(A\)的匹配指针\(p\)

每次插入一个字符\(c\),找到\(A\)\(p+1\)之后第一个字符\(c\),令匹配指针跳过去

可以预处理出这样的下一个字符\(nxt_{i,c}\),完成\(O(1)\)匹配

除此以外,我们还需要对于本质不同去重

如果用\(\text{trie}\)树去重,需要开一个\(\frac{n^2}{2}\cdot 26\)的数组,面临着内存不够的问题

你可以信仰不开这么大

也可以去学习一下\(\text{DAT(Double Array Trie)}\)算法

也可以用\(\text{hash+set/map/hash table/sort unique}\)

也可以用链表暴力存储trie树的情况,每次暴力for过去找儿子

这样内存均为\(O(n^2)\)

以下是链表trie树的版本

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair <int,int> Pii;
#define reg register
#define mp make_pair
#define pb push_back
#define Mod1(x) ((x>=P)&&(x-=P))
#define Mod2(x) ((x<0)&&(x+=P))
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }

char IO;
template <class T=int> T rd(){
	T s=0; int f=0;
	while(!isdigit(IO=getchar())) f|=IO=='-';
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=3010,M=N*N/2,INF=1e9+10;
bool Mbe;

int n;
int nxt[N][26];
char A[N],B[N];
struct Edge{ int c,to,nxt; } e[M];
int head[M],cnt;

bool Med;
int main(){
	//fprintf(stderr,"%.2lf\n",(&Med-&Mbe)/1024.0/1024.0);
	freopen("block.in","r",stdin),freopen("block.out","w",stdout);
	n=rd(),scanf("%s%s",A+1,B+1);
	drep(i,n,1) {
		rep(j,0,25) nxt[i][j]=nxt[i+1][j];
		nxt[i][A[i]-'a']=i;
	}
	rep(i,1,n) {
		int u=0,p=0;
		rep(j,i,n) {
			int c=B[j]-'a';
			if(!(p=nxt[p+1][c])) break;
			int v=-1;
			for(int k=head[u];k;k=e[k].nxt) if(e[k].c==c) { v=e[k].to; break; }
			if(~v) u=v;
			else {
				v=++cnt;
				e[v]=(Edge){c,v,head[u]};
				head[u]=v,u=v;
			}
		}
	}
	printf("%d\n",cnt);
}
posted @ 2021-03-27 12:15  chasedeath  阅读(122)  评论(0编辑  收藏  举报