题意:求一个串中可重叠至少出现m次的最长子串,并且求出该串最后一次出现的起始位置。
找了一下网上并没有SAM做法的题解。。我来说一下好了
首先每个SAM上的结点需要多保存两个值:cnt和right。cnt代表该状态right集合大小,right值是right集合中最大的那个值(right集合定义见CLJ ppt),初值为val。
其实结点如果是复制的结点(即代码中nq)的话初值应该是原结点的right?。。没关系,我们总会更新到的
那么我们就有了两种做法:1、每次插入完暴力往上更新父亲结点的right集合大小。
2、插入完之后统一更新(如SPOJ 8222)
第一种做法是有问题的,复杂度得不到保证,于是妥妥TLE。
第二种相对比较麻烦。。由于val值大的可以更新val值小的cnt,那么先根据val值进行一次排序。然后因为所有子串都是某个前缀的某个后缀,那么我们把前缀的cnt设置为1(从网上讲解的图来看,就是最中间那一排结点cnt值全部设置为1,其它设置为0),按照val从大到小更新一下cnt和right即可。
怕跪掉所以对m=1进行了特判。。其实不特判也是可以的
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 const int MAXN=40000+5; 5 const int SIGMA_SIZE=26; 6 const int INF=~0U>>1; 7 struct State{ 8 State* go[SIGMA_SIZE],*suf; 9 int val,cnt,right; 10 State():suf(0) {val=cnt=0;memset(go,0,sizeof go);} 11 }*root,*last; 12 State mem[MAXN<<1],*cur; 13 int ans1,ans2; 14 int m; 15 inline void init() 16 { 17 ans1=-1; 18 cur=mem; 19 mem[0]=State(); 20 last=root=cur++; 21 } 22 inline void extend(int w) 23 { 24 State* p=last,*np=cur++; 25 *np=State(); 26 np->right=np->val=p->val+1; 27 while(p && !p->go[w]) p->go[w]=np,p=p->suf; 28 if(!p) np->suf=root; 29 else 30 { 31 State* q=p->go[w]; 32 if(q->val==p->val+1) np->suf=q; 33 else 34 { 35 State* nq=cur++; 36 *nq=State(); 37 memcpy(nq->go,q->go,sizeof q->go); 38 nq->right=nq->val=p->val+1; 39 nq->suf=q->suf; 40 q->suf=np->suf=nq; 41 while(p && p->go[w]==q) p->go[w]=nq,p=p->suf; 42 } 43 } 44 last=np; 45 } 46 inline int idx(char c) 47 { 48 return c-‘a‘; 49 } 50 char s[MAXN]; 51 int n; 52 State* pt[MAXN<<1]; 53 void work() 54 { 55 static int ws[MAXN<<1]; 56 State* t; 57 for(int i=0;i<=n;++i) ws[i]=0; 58 for(t=mem+1;t!=cur;++t) ++ws[t->val]; 59 for(int i=1;i<=n;++i) ws[i]+=ws[i-1]; 60 for(t=cur-1;t!=mem;--t) pt[--ws[t->val]]=t; 61 t=root; 62 for(int i=0;i<n;++i) t=t->go[idx(s[i])],t->cnt++; 63 for(int i=cur-mem-2;i>=0;--i) 64 { 65 State* u=pt[i]; 66 if(u->cnt>=m) 67 { 68 if(u->val>ans1) ans1=u->val,ans2=u->right-u->val; 69 else if(u->val==ans1) ans2=std::max(ans2,u->right-u->val); 70 } 71 if(u->suf) 72 u->suf->cnt+=u->cnt,u->suf->right=std::max(u->suf->right,u->right); 73 } 74 } 75 int main() 76 { 77 //freopen("1.in","r",stdin); 78 while(scanf("%d",&m)!=EOF && m) 79 { 80 init(); 81 scanf("%s",s); 82 n=strlen(s); 83 if(m==1) 84 { 85 printf("%d 0\n",n); 86 continue; 87 } 88 for(int i=0;i<n;++i) 89 extend(idx(s[i])); 90 work(); 91 if(ans1==-1) puts("none"); 92 else printf("%d %d\n",ans1,ans2); 93 } 94 return 0; 95 }
时间: 2024-11-09 01:46:23