巨佬博客: https://www.cnblogs.com/zwfymqz/p/8413523.html
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e6+10; int n,M,sa[N],rk[N],tp[N],sum[N],hight[N]; char s[N]; void Qsort() { for(int i=0;i<=M;i++) sum[i]=0; for(int i=1;i<=n;i++) sum[rk[i]]++; for(int i=1;i<=M;i++) sum[i]+=sum[i-1]; for(int i=n;i>=1;i--) sa[ sum[rk[tp[i]]]-- ]=tp[i]; } void suffixsort() { M=100; for(int i=1;i<=n;i++) rk[i]=s[i]-‘0‘+1,tp[i]=i; Qsort(); for(int w=1,p=0;p<n;M=p,w<<=1) { p=0; for(int i=1;i<=w;i++)tp[++p]=n-w+i; for(int i=1;i<=n;i++) if(sa[i]>w)tp[++p]=sa[i]-w; Qsort(); swap(tp,rk); rk[sa[1]]=p=1; for(int i=2;i<=n;i++) rk[sa[i]]=( tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w] )?p:++p; } } void gethight() { int j,k=0; for(int i=1;i<=n;i++) { if(k)k--; int j=sa[rk[i]-1]; while(s[i+k]==s[j+k])k++; hight[rk[i]]=k; } } int main() { scanf("%s",s+1);n=strlen(s+1); suffixsort(); for(int i=1;i<=n;i++) printf("%d ",sa[i]); }
后缀数组模板
$sa[i]:排名为i的后缀的位置$
$rk[i]:从第i个位置开始的后缀的排名,下文为了叙述方便,把从第i个位置开始的后缀简称为后缀i$
$tp[i]:基数排序的第二关键字,意义与sa一样,即第二关键字排名为i的后缀的位置$
$sum[i]:ii号元素出现了多少次。辅助基数排序$
$s:字符串,s[i]表示字符串中第i个字符串$
$height[i]:lcp(sa[i],sa[i−1]),即排名为i的后缀与排名为i−1的后缀的最长公共前缀$
Long Long Message POJ - 2774
题意:
$一个人读取同一个字符串两次,因为机器的问题,每次读取可能会在原串的左边和右边加上一串随机字符串, 问原串的最大长度$
题解:
- $将两个串合并成一个串$
- $求出height, 所以只要遍历所有的height 求出最大值即可 注意判定height所表示的第i串和第i-1串是否在两个串里$
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<cstring> #include<string> using namespace std; typedef long long ll; const int N=2e5+10; int n,M,sa[N],rk[N],tp[N],sum[N],height[N],lens,lens1; char s[N],s1[N]; void Qsort() { for(int i=0;i<=M;i++) sum[i]=0; for(int i=1;i<=n;i++) sum[rk[i]]++; for(int i=1;i<=M;i++) sum[i]+=sum[i-1]; for(int i=n;i>=1;i--) sa[ sum[rk[tp[i]]]-- ]=tp[i]; } void suffixsort() { M=100; for(int i=1;i<=n;i++) rk[i]=s[i]-‘0‘+1,tp[i]=i; Qsort(); for(int w=1,p=0;p<n;M=p,w<<=1) { p=0; for(int i=1;i<=w;i++)tp[++p]=n-w+i; for(int i=1;i<=n;i++) if(sa[i]>w)tp[++p]=sa[i]-w; Qsort(); swap(tp,rk); rk[sa[1]]=p=1; for(int i=2;i<=n;i++) rk[sa[i]]=( tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w] )?p:++p; } } void getheight() { int j,k=0; for(int i=1;i<=n;i++) { if(k)k--; int j=sa[rk[i]-1]; while(s[i+k]==s[j+k])k++; height[rk[i]]=k; } } bool ok(int x,int y) { if(x>y)swap(x,y); return x>=1&&x<=lens&&y>=lens+1&&y<=lens+lens1; } int main() { scanf("%s",s+1); lens=strlen(s+1); scanf("%s",s1+1); lens1=strlen(s1+1); strcat(s+1,s1+1); n=lens+lens1; suffixsort();getheight();int ans=0; for(int i=2;i<=lens+lens1;i++) if(ok(sa[i],sa[i-1]))ans=max(ans,height[i]); cout<<ans; }
牛奶模式Milk Patterns P2852
题意:
$求出给定串的重复 k 次的重复子串,使之长度最大化 $
题解:
- 和上面那题非常类似 只要选k个后缀 求其最大公共前缀长度 ,并使之最大化即可
- 用单调栈跑height数组即可 单调栈维护长度为k-1的最小值
- 注意最好离散化 不然基数排序容易T
- 求相同子串肯定是和height有关
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+10; int n,M,sa[N],rk[N],tp[N],sum[N],height[N],lens,lens1; int s[N],b[N],st[N],k,ans=0; void Qsort() { for(int i=0;i<=M;i++) sum[i]=0; for(int i=1;i<=n;i++) sum[rk[i]]++; for(int i=1;i<=M;i++) sum[i]+=sum[i-1]; for(int i=n;i>=1;i--) sa[ sum[rk[tp[i]]]-- ]=tp[i]; } void suffixsort() { for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i; Qsort(); for(int w=1,p=0;p<n;M=p,w<<=1) { p=0; for(int i=1;i<=w;i++)tp[++p]=n-w+i; for(int i=1;i<=n;i++) if(sa[i]>w)tp[++p]=sa[i]-w; Qsort(); swap(tp,rk); rk[sa[1]]=p=1; for(int i=2;i<=n;i++) rk[sa[i]]=( tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w] )?p:++p; } } void getheight() { int j,k=0; for(int i=1;i<=n;i++) { if(k)k--; int j=sa[rk[i]-1]; while(s[i+k]==s[j+k])k++; height[rk[i]]=k; } } int main() { cin>>n>>k;k--; for(int i=1;i<=n;i++)scanf("%d",&s[i]),b[i]=s[i]; sort(b+1,b+1+n); int nn=unique(b+1,b+1+n)-b-1;M=nn; for(int i=1;i<=n;i++)s[i]=lower_bound(b+1,b+1+nn,s[i])-b; suffixsort();getheight(); int l=1,r=0; for(int i=2;i<=n;i++) { if(l<=r&&st[l]<i-k+1)l++; while(l<=r&&height[st[r]]>height[i])r--; st[++r]=i; ans=max(ans,height[st[l]]); } cout<<ans; }
原文地址:https://www.cnblogs.com/bxd123/p/11793833.html
时间: 2024-10-12 09:16:28