POJ 3882/LA4513/HDU4080/ZOJ3395 Stammering Aliens 题解【后缀自动机】
2015-04-14 12:58
155 查看
题意:求一个串中可重叠至少出现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进行了特判。。其实不特判也是可以的
View Code
找了一下网上并没有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进行了特判。。其实不特判也是可以的
#include<cstdio> #include<algorithm> #include<cstring> const int MAXN=40000+5; const int SIGMA_SIZE=26; const int INF=~0U>>1; struct State{ State* go[SIGMA_SIZE],*suf; int val,cnt,right; State():suf(0) {val=cnt=0;memset(go,0,sizeof go);} }*root,*last; State mem[MAXN<<1],*cur; int ans1,ans2; int m; inline void init() { ans1=-1; cur=mem; mem[0]=State(); last=root=cur++; } inline void extend(int w) { State* p=last,*np=cur++; *np=State(); np->right=np->val=p->val+1; while(p && !p->go[w]) p->go[w]=np,p=p->suf; if(!p) np->suf=root; else { State* q=p->go[w]; if(q->val==p->val+1) np->suf=q; else { State* nq=cur++; *nq=State(); memcpy(nq->go,q->go,sizeof q->go); nq->right=nq->val=p->val+1; nq->suf=q->suf; q->suf=np->suf=nq; while(p && p->go[w]==q) p->go[w]=nq,p=p->suf; } } last=np; } inline int idx(char c) { return c-'a'; } char s[MAXN]; int n; State* pt[MAXN<<1]; void work() { static int ws[MAXN<<1]; State* t; for(int i=0;i<=n;++i) ws[i]=0; for(t=mem+1;t!=cur;++t) ++ws[t->val]; for(int i=1;i<=n;++i) ws[i]+=ws[i-1]; for(t=cur-1;t!=mem;--t) pt[--ws[t->val]]=t; t=root; for(int i=0;i<n;++i) t=t->go[idx(s[i])],t->cnt++; for(int i=cur-mem-2;i>=0;--i) { State* u=pt[i]; if(u->cnt>=m) { if(u->val>ans1) ans1=u->val,ans2=u->right-u->val; else if(u->val==ans1) ans2=std::max(ans2,u->right-u->val); } if(u->suf) u->suf->cnt+=u->cnt,u->suf->right=std::max(u->suf->right,u->right); } } int main() { //freopen("1.in","r",stdin); while(scanf("%d",&m)!=EOF && m) { init(); scanf("%s",s); n=strlen(s); if(m==1) { printf("%d 0\n",n); continue; } for(int i=0;i<n;++i) extend(idx(s[i])); work(); if(ans1==-1) puts("none"); else printf("%d %d\n",ans1,ans2); } return 0; }
View Code
相关文章推荐
- poj 3080 后缀自动机
- POJ1509 Glass Beads 【后缀自动机】
- poj 3415Common Substrings (后缀自动机)
- poj 3882 后缀数组 求一个串至少出现k次的最长重复子串的长度
- Hdu 4080 & Poj 3882 Stammering Aliens (后缀数组 可重叠k次最长重复子串)
- POJ 3415 (后缀自动机)
- 【后缀自动机】POJ1509[Glass Beads]题解
- POJ 3518 (后缀自动机)
- poj1743 Musical Theme(后缀数组|后缀自动机)
- POJ1743 Musical Theme [后缀自动机]
- 【后缀自动机】SPOJ(LCS)[Longest Common Substring]题解
- poj 2774 最长公共子串--字符串hash或者后缀数组或者后缀自动机
- poj 1743 Musical Theme 后缀自动机/后缀数组/后缀树
- [后缀自动机] POJ 1743 Musical Theme
- POJ 3415 Common Substrings【后缀自动机】
- [POJ1509]Glass Beads && 后缀自动机
- POJ 1509 Glass Beads【后缀自动机、最小表示法】
- POJ-1509 Glass Beads (字符串最小表示法&后缀自动机)
- 【后缀自动机】SPOJ(LCS2)[Longest Common Substring II]题解
- poj 3415 SAM后缀自动机