后缀数组(1)

感觉跟字符串有关的算法都难飞了:)

首先入坑是noi day2t2,舔题解开启后缀数组副本

  • 首先自然是模板题刷水,因为(爱情怎莫会有沧桑)懒,所以没写过基数排序,所以舔模板也舔地十分困难,最后还是跪求zl老爷讲解,,,然而当时也是美得朦胧。。。

   不过还好之前舔了集训队论文->算法合集之《后缀数组——处理字符串的有力工具》

   

 1 #include<stdio.h>
 2 #include<string.h>
 3 #define maxn 100005
 4 char str[maxn];
 5 int sa[maxn],x[maxn],y[maxn],tong[maxn],rank[maxn],h[maxn];
 6 bool cmp(int *nxt,int a,int b,int l){
 7     return nxt[a]==nxt[b]&&nxt[a+l]==nxt[b+l];
 8 }
 9 void SA(int n,int m){
10     int i,j,*cur=x,*nxt=y,*t,p;
11     for(i=0;i<m;i++)tong[i]=0;
12     for(i=0;i<n;i++)tong[cur[i]=str[i]]++;
13     for(i=1;i<m;i++)tong[i]+=tong[i-1];
14     for(i=n-1;i>=0;i--)sa[--tong[cur[i]]]=i;
15
16     for(j=1,p=1;p<n;j*=2,m=p){
17
18         for(i=n-j,p=0;i<n;i++)nxt[p++]=i;
19         for(i=0;i<n;i++)if(sa[i]>=j)nxt[p++]=sa[i]-j;
20
21         for(i=0;i<m;i++)tong[i]=0;
22         for(i=0;i<n;i++)tong[cur[nxt[i]]]++;
23         for(i=1;i<m;i++)tong[i]+=tong[i-1];
24         for(i=n-1;i>=0;i--)sa[--tong[cur[nxt[i]]]]=nxt[i];
25         for(t=cur,cur=nxt,nxt=t,p=1,cur[sa[0]]=0,i=1;i<n;i++)
26             cur[sa[i]]=cmp(nxt,sa[i-1],sa[i],j)?p-1:p++;
27     }
28 }
29 void calheight(int n){
30     int i,j,k=0;
31     for(i=1;i<=n;i++)rank[sa[i]]=i;
32     for(i=0;i<n;h[rank[i++]]=k)
33         for(k?k--:0,j=sa[rank[i]-1];str[i+k]==str[j+k];k++);
34 }
35 int main(){
36     freopen("1.in","r",stdin);
37     scanf("%s",str);
38     int n=strlen(str);
39     str[n]=0;
40     SA(n+1,128);
41     for(int i=1;i<=n;i++)printf("%d ",sa[i]+1);
42     printf("\n");
43     calheight(n);
44     for(int i=2;i<=n;i++)printf("%d ",h[i]);
45     return 0;
46 }

     代码的核心就是那三个空行,,,把SA函数分成了四个部分

     ①是处理长度为1的字符的大小关系

     ②是倍增循环2^j里的j

     ③是根据上层的sa数组排第二关键字,nxt里放的就是按第二关键字排好序的第一关键字对应位置,,,其实就是图中的斜杠对应的竖杠。。。

     ④是对第一关键字的排序,cmp是允许编号重复

   tips:论文里的wv数组被老夫给压了(小机房日常之比谁代码短),然而gst大爷说不压会更快,所以之后的某篇里会出现haha数组。。。

  既然有课件于是就按着刷呗 

  • 单个字符串相关问题

    • 最长公共前缀,即lcp(suffix(i),suffix(j))=min{height[k]|i+1≤k≤j},RMQ处理
    • 可重叠最长重复子串,即求height数组里的最大值即可,因为任意两个后缀的最长公共前缀都是height数组里某一段的最小值,那么这个值一定不大于height数组里的最大值
    • 不可重叠最长重复子串,二分,划分区间,判断是否相交

      poj1743

        

 1 #include<stdio.h>
 2 #include<algorithm>
 3 using namespace std;
 4
 5 #define maxn 20005
 6 int str[maxn],sa[maxn],rank[maxn],height[maxn],x[maxn],y[maxn],tong[maxn];
 7
 8 bool cmp(int *nxt,int a,int b,int j){
 9     return nxt[a]==nxt[b]&&nxt[a+j]==nxt[b+j];
10 }
11 void SA(int n,int m){
12     int i,j,p,*cur=x,*nxt=y,*t;
13     for(i=0;i<m;i++)tong[i]=0;
14     for(i=0;i<n;i++)tong[cur[i]=str[i]]++;
15     for(i=1;i<m;i++)tong[i]+=tong[i-1];
16     for(i=n-1;i>=0;i--)sa[--tong[cur[i]]]=i;
17
18     for(p=1,j=1;p<n;j*=2,m=p){
19
20         for(p=0,i=n-j;i<n;i++)nxt[p++]=i;
21         for(i=0;i<n;i++)if(sa[i]>=j)nxt[p++]=sa[i]-j;
22
23         for(i=0;i<m;i++)tong[i]=0;
24         for(i=0;i<n;i++)tong[cur[nxt[i]]]++;
25         for(i=1;i<m;i++)tong[i]+=tong[i-1];
26         for(i=n-1;i>=0;i--)sa[--tong[cur[nxt[i]]]]=nxt[i];
27         for(t=cur,cur=nxt,nxt=t,p=1,cur[sa[0]]=0,i=1;i<n;i++)
28             cur[sa[i]]=cmp(nxt,sa[i-1],sa[i],j)?p-1:p++;
29     }
30 }
31 void calheight(int n){
32     int i,j,k=0;
33     for(i=1;i<=n;i++)rank[sa[i]]=i;
34     for(i=0;i<n;height[rank[i++]]=k)
35         for(k?k--:0,j=sa[rank[i]-1];str[i+k]==str[j+k];k++);
36 }
37 bool can(int x,int n){
38     int i=2,maxp,minp;
39     while(1){
40         while(i<=n&&height[i]<x)i++;
41         if(i>n)break;
42         maxp=sa[i-1];
43         minp=sa[i-1];
44         while(i<=n&&height[i]>=x){
45             maxp=max(maxp,sa[i]);
46             minp=min(minp,sa[i]);
47             i++;
48         }
49         if(maxp-minp>=x)return true;
50     }
51     return false;
52 }
53 int main(){
54     //freopen("1.in","r",stdin);
55     while(1){
56         int n;
57         scanf("%d",&n);
58     if(!n)break;
59     for(int i=0;i<n;i++)scanf("%d",&str[i]);
60         if(n<10){
61             printf("0\n");
62             continue;
63         }
64         n--;
65         for(int i=0;i<n;i++)str[i]=str[i+1]-str[i]+89;
66         str[n]=0;
67         SA(n+1,200);
68         calheight(n);
69         int l=0,r=n/2+1;
70         while(l<r-1){
71             int mid=(l+r)>>1;
72             if(can(mid,n))l=mid;
73             else r=mid;
74         }
75         l=l<4?0:l+1;
76         printf("%d\n",l);
77     }
78     return 0;
79 }

        tips:①因为一个子串加减一个值与原子串视为同一个,所以要用差值来计算,还要映射为正数

           ②当n=1时要特判,或者与n<10的情况一起判掉

    • 可重叠的k次最长重复子串,同上,只不过判断条件变成了划分的元素个数与k的关系

      poj3261

        没撒可注意的,m有点大,不会写快排版的烧饼就只能离散咯(波浪号

        

 1 #include<stdio.h>
 2 #include<algorithm>
 3 using namespace std;
 4
 5 #define maxn 20005
 6 #define fir first
 7 #define sec second
 8 pair<int,int>cow[maxn];
 9 int str[maxn],sa[maxn],rank[maxn],height[maxn],x[maxn],y[maxn],tong[maxn];
10
11 bool cmp(int *nxt,int a,int b,int j){
12     return nxt[a]==nxt[b]&&nxt[a+j]==nxt[b+j];
13 }
14 void SA(int n,int m){
15     int i,j,p,*cur=x,*nxt=y,*t;
16     for(i=0;i<m;i++)tong[i]=0;
17     for(i=0;i<n;i++)tong[cur[i]=str[i]]++;
18     for(i=1;i<m;i++)tong[i]+=tong[i-1];
19     for(i=n-1;i>=0;i--)sa[--tong[cur[i]]]=i;
20
21     for(p=1,j=1;p<n;j*=2,m=p){
22
23         for(p=0,i=n-j;i<n;i++)nxt[p++]=i;
24         for(i=0;i<n;i++)if(sa[i]>=j)nxt[p++]=sa[i]-j;
25
26         for(i=0;i<m;i++)tong[i]=0;
27         for(i=0;i<n;i++)tong[cur[nxt[i]]]++;
28         for(i=1;i<m;i++)tong[i]+=tong[i-1];
29         for(i=n-1;i>=0;i--)sa[--tong[cur[nxt[i]]]]=nxt[i];
30         for(t=cur,cur=nxt,nxt=t,p=1,cur[sa[0]]=0,i=1;i<n;i++)
31             cur[sa[i]]=cmp(nxt,sa[i-1],sa[i],j)?p-1:p++;
32     }
33 }
34 void calheight(int n){
35     int i,j,k=0;
36     for(i=1;i<=n;i++)rank[sa[i]]=i;
37     for(i=0;i<n;height[rank[i++]]=k)
38         for(k?k--:0,j=sa[rank[i]-1];str[i+k]==str[j+k];k++);
39 }
40 bool can(int x,int n,int k){
41     int i=2;
42     while(1){
43         while(i<=n&&height[i]<x)i++;
44         if(i>n)break;
45         int num=1;
46         while(i<=n&&height[i]>=x)num++,i++;
47         if(num>=k)return true;
48     }
49     return false;
50 }
51 int main(){
52     freopen("1.in","r",stdin);
53     int n,k;
54     scanf("%d%d",&n,&k);
55     for(int i=0;i<n;i++){
56         scanf("%d",&cow[i].fir);
57         cow[i].sec=i;
58     }
59     sort(cow,cow+n);
60     int m=1;
61     str[cow[0].sec]=m;
62     for(int i=1;i<n;i++){
63         if(cow[i].fir!=cow[i-1].fir)m++;
64         str[cow[i].sec]=m;
65     }
66     str[n]=0;
67     SA(n+1,m+1);
68     calheight(n);
69     int l=0,r=n;
70     while(l<r-1){
71         int mid=(l+r)>>1;
72         if(can(mid,n,k))l=mid;
73         else r=mid;
74     }
75     printf("%d\n",l);
76     return 0;
77 }

  第一篇博客finish,老夫碎觉去鸟

时间: 2024-10-27 17:02:22

后缀数组(1)的相关文章

SPOJ 705 Distinct Substrings(后缀数组)

[题目链接] http://www.spoj.com/problems/SUBST1/ [题目大意] 给出一个串,求出不相同的子串的个数. [题解] 对原串做一遍后缀数组,按照后缀的名次进行遍历, 每个后缀对答案的贡献为n-sa[i]+1-h[i], 因为排名相邻的后缀一定是公共前缀最长的, 那么就可以有效地通过LCP去除重复计算的子串. [代码] #include <cstdio> #include <cstring> #include <algorithm> usi

hdu5769--Substring(后缀数组)

题意:求含有某个字母的某个字符串的不同子串的个数 题解:后缀数组,记录每个位置距离需要出现的字母的距离就可以了.因为不太了解后缀模版卡了一会,还是很简单的. 记住sa和height数组都是1-n的下标. //后缀数组 #include <stdio.h> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll;

hdu 3518 Boring counting 后缀数组LCP

题目链接 题意:给定长度为n(n <= 1000)的只含小写字母的字符串,问字符串子串不重叠出现最少两次的不同子串个数; input: aaaa ababcabb aaaaaa # output 2 3 3 思路:套用后缀数组求解出sa数组和height数组,之后枚举后缀的公共前缀长度i,由于不能重叠,所以计数的是相邻height不满足LCP >= i的. 写写对后缀数组倍增算法的理解: 1.如果要sa数组对应的值也是1~n就需要在最后加上一个最小的且不出现的字符'#',里面y[]是利用sa数

【tyvj1860】后缀数组

描述 我们定义一个字符串的后缀suffix(i)表示从s[i]到s[length(s)]这段子串.后缀数组(Suffix array)SA[i]中存放着一个排列,满足suffix(sa[i])<suffix(sa[i+1]) 按照字典序方式比较定义height[i]表示suffix(sa[i])与suffix(sa[i-1])之间的最长公共前缀长度,其中height[1]=0你的任务就是求出SA和height这两个数组.字符串长度<=200000 输入格式 一行,为描述中的字符串(仅会出现小写

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈

题目大意: 思路:一看各种后缀那就是后缀数组没跑了. 求出sa,height之后就可以乱搞了.对于height数组中的一个值,height[i]来说,这个值能够作为lcp值的作用域只在左边第一个比他小的位置到右边第一个比他小的位置.这个东西很明显可以倍增RMQ+二分/单调栈. 之后就是数学题了 Σlen[Ti] + len[Tj] = (len + 1) * len * (len - 1),之后吧所有求出来的Σ2 * lcp(Ti,Tj)减掉就是答案. 记得答案开long long CODE:

hdu 5030 Rabbit&#39;s String(后缀数组&amp;二分)

Rabbit's String Time Limit: 40000/20000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 288    Accepted Submission(s): 108 Problem Description Long long ago, there lived a lot of rabbits in the forest. One day, the

hdu 4416 Good Article Good sentence(后缀数组&amp;思维)

Good Article Good sentence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2308    Accepted Submission(s): 649 Problem Description In middle school, teachers used to encourage us to pick up pre

uva 10829 - L-Gap Substrings(后缀数组)

题目链接:uva 10829 - L-Gap Substrings 题目大意:给定一个字符串,问有多少字符串满足UVU的形式,要求U非空,V的长度为g. 解题思路:对字符串的正序和逆序构建后缀数组,然后枚举U的长度l,每次以长度l分区间,在l和l+d+g所在的两个区间上确定U的最大长度. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using nam

poj 3693 Maximum repetition substring(后缀数组)

题目链接:poj 3693 Maximum repetition substring 题目大意:求一个字符串中循环子串次数最多的子串. 解题思路:对字符串构建后缀数组,然后枚举循环长度,分区间确定.对于一个长度l,每次求出i和i+l的LCP,那么以i为起点,循环子串长度为l的子串的循环次数为LCP/l+1,然后再考虑一下从i-l+1~i之间有没有存在增长的可能性. #include <cstdio> #include <cstring> #include <vector>

uva 10526 - Intellectual Property(后缀数组)

题目链接:uva 10526 - Intellectual Property 题目大意:给定两个文本,问说下面一个文本中在哪些位置上抄袭了上面个一个文本的,输出n个抄袭位置(不足n个情况全部输出),按照长度优先输出,长度相同的输出位置靠前的. 注意:空格,回车都算一个字符:一段字符只能是抄袭上面的一部分,比如上:NSB*SB 下:NSB 答案:NSB. 解题思路:将两个文本连接在一起,中间用没有出现的字符分割,然后处理处后缀数组,根据height数组的性质,求出哪些位置匹配的长度不为0(注意匹配