POJ 3882/LA4513/HDU4080/ZOJ3395 Stammering Aliens 题解【后缀自动机】

题意:求一个串中可重叠至少出现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

POJ 3882/LA4513/HDU4080/ZOJ3395 Stammering Aliens 题解【后缀自动机】的相关文章

poj 3882(Stammering Aliens) 后缀数组 或者 hash

后缀数组:  构建后缀数组,注意要在字符串莫末尾加上一个没出现过的字符.然后可以2分或者直接扫描,直接扫描需要用单调队列来维护 VIEW CODE #include<cstdio> #include<algorithm> #include<iostream> #include<cmath> #include<queue> #include<stack> #include<string> #include<cstrin

POJ 3882 Stammering Aliens 后缀数组height应用

题目来源:POJ 3882 Stammering Aliens 题意:给你m一个一个字符串 求至少出现m次的最长字符串 可以在字符串中重叠出现 思路:二分长度l 然后从height数组中找长度大于等于l的前缀 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 40010; char s[maxn]; int sa[maxn]; i

后缀数组(至少重复k次的可重叠的最长重复子串)—— POJ 3882

对应POJ 题目:点击打开链接 Stammering Aliens Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Description Dr. Ellie Arroway has established contact with an extraterrestrial civilization. However, all efforts to decode their m

uva 12206 - Stammering Aliens(哈希)

题目链接:uva 12206 - Stammering Aliens 题目大意:给出一个字符串,找出至少出现m次的最长子串. 解题思路:哈希算法,将每个后缀数组建立一个哈希值,每次二分长度判断,每次判断时将哈希值排序,计数即可. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef unsigned long long ll; const int ma

UVA 12206 - Stammering Aliens(后缀数组)

UVA 12206 - Stammering Aliens 题目链接 题意:给定一个序列,求出出现次数大于m,长度最长的子串的最大下标 思路:后缀数组,搞出height数组后,利用二分去查找即可 这题之前还写过hash的写法也能过,不过写后缀数组的时候,犯了一个傻逼错误,把none输出成node还一直找不到...这是刷题来第二次碰到这种逗比错误了,还是得注意.. 代码: #include <cstdio> #include <cstring> #include <algori

LA 4513 Stammering Aliens 字符串hash

字符串hash模板, 本题是求,给定字符串s中至少出现m次的最长字符串长度,及此时起始位置的最大值 LA 4513  Stammering Aliens 白书p225 //#pragma warning (disable: 4786) //#pragma comment (linker, "/STACK:16777216") //HEAD #include <cstdio> #include <ctime> #include <cstdlib> #i

POJ 2914 Minimum Cut 最小割算法题解

最标准的最小割算法应用题目. 核心思想就是缩边:先缩小最大的边,然后缩小次大的边,依此缩小 基础算法:Prime最小生成树算法 不过本题测试的数据好像怪怪的,同样的算法时间运行会差别很大,而且一样的代码替换,居然会WA.系统出错的几率很小,难倒测试系统本题会有错误? 懒得继续测试这道题的系统了,反正算法正确,AC. #include <stdio.h> #include <string.h> #include <limits.h> const int MAX_N = 5

poj 1743 最长不重叠重复子串 后缀数组+lcp+二分

题比较容易读懂,但是建模需动点脑子: 一个子串加常数形成的子串认为跟子串相同,求最长不重叠重复子串 题目中说 is disjoint from (i.e., non-overlapping with) at least one of its other appearance(s) 意味着不能重叠,举个例子 1, 2,3,  52, 53,54 1,2, 3和 52, 53,54满足题意,差值为51 枚举差值肯定不行------看了题解明白的:: 后项减去前一项得到: 1,1,1,49,1,1  

poj 2774 最长公共子串--字符串hash或者后缀数组或者后缀自动机

http://poj.org/problem?id=2774 想用后缀数组的看这里:http://blog.csdn.net/u011026968/article/details/22801015 本文主要讲下怎么hash去找 开始的时候写的是O(n^2 logn)算法 果断超时...虽然也用了二分的,, 代码如下: //hash+二分 #include <cstdio> #include <cstring> #include <algorithm> #include