【字典树】新年礼物




大致思路

先理解这道题:子序列是说把这几个符合要求字符串排成队,看这条队有多少个字符串。

之前我的思路就是:用字典树边插入的时候边找,找到前缀相同的我就看看你是不是同时前缀也是后缀(用了遍历比较)。然后数据量一大,又是二维数组,还是内存超限了。

后来看了答案,发现问题主要是出在优化方式上:

    ①采用链表代替字典树数组。字典树中的一个节点由结构体Node来代替,用指针的形式来达到之前用数组“转移到子节点”的效果。

    ②找相同前缀当然还是使用字典树啦~但是相同后缀的比较你不要蠢啊...exkmp的nextt数组(打表方法肯定比每个都去循环快)用起走啊!!!!


我的代码(注释还是有点东西的)

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
//不用二维数组来存字典树而是用指针 结构体!! 
//不用一个一个比前缀==后缀,用上学过的exkmp!! 
int nextt[2000000];
struct Node
{
	int maxx=0; //记录当前以此节点结尾的子序列的最长长度
	bool isleaf=false;
	Node *leaves[26];
	Node * insert(char a)  
	{
		return leaves[a-'A']=new Node; //① 
	}
	Node()
	{
		memset(leaves,0,sizeof(leaves)); //②.相当于初始化为NULL 
		isleaf=false;
		maxx=0;
	}
}head;

void getnext(char *t,int n)  //exkmp
{
	nextt[1]=n;
	int p=0;
	while(t[p]==t[p+1] && p<=n) p++;
	nextt[2]=p;
	int k=2,l;
	for(int i=3;i<=n;i++)
	{
		p=k+nextt[k]-1;
		l=nextt[i-k+1];
		if(p-i>=l) nextt[i]=l;
		else
		{
			int j=p-i+1;
			if(j<0) j=0;
			while(i+j<=n && t[j+1]==t[i+j]) j++;
			nextt[i]=j;
		}
		k=i;
	}
}

int maxx=0;
void insert(char *t,int len) //字典树插入 
{
	bool flag=false; //记录有没有变化。相当于之前的temp==tot 
	Node *p=&head;
	int dp=1;
	for(int i=1;i<=len;i++)
	{
		if(p->leaves[t[i]-'A']==NULL) 
		{
			p=p->insert(t[i]);  //新开结点 作转移 
			flag=true; //有变动 
		}
		else
			p=p->leaves[t[i]-'A']; //转移
		if(flag==false && p->isleaf==true && nextt[len-i+1]==i)
		{
			dp=max(p->maxx+1,dp);
		}
	}
	p->isleaf=true;
	p->maxx=dp;
	maxx=max(dp,maxx);
}

char t[2000010];
int main()
{
	int N;
	cin>>N;
	while(N--)
	{
		scanf("%s",t+1); //每次都刷新字符数组的全部~而不是作部分覆盖~ 
		getnext(t,strlen(t+1));
		insert(t,strlen(t+1));
	}
	cout<<maxx;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_38033475/article/details/80316063