后缀数组
感觉跟字符串有关的算法都难飞了:)
首先入坑是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 }