大致思路:
先理解这道题:子序列是说把这几个符合要求字符串排成队,看这条队有多少个字符串。
之前我的思路就是:用字典树边插入的时候边找,找到前缀相同的我就看看你是不是同时前缀也是后缀(用了遍历比较)。然后数据量一大,又是二维数组,还是内存超限了。
后来看了答案,发现问题主要是出在优化方式上:
①采用链表代替字典树数组。字典树中的一个节点由结构体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; }