P3809 【模板】后缀排序
题意:给定字符串,输出后缀数组就可以了
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <cstring>
#include <string>
using namespace std;
const int maxn=1e6+5;
struct suffixvec
{
int c[maxn],sa[maxn],rank[maxn],height[maxn],top[maxn];
int m,len;
char s[maxn];
void init(int len1,char *s1)
{
len=len1;
strcpy(s+1,s1+1);
}
void clear()
{
for(int i=1;i<=len;++i)
sa[i]=rank[i]=height[i]=0;
}
void qsort()
{
for(int i=0;i<=m;++i)
c[i]=0;
for(int i=1;i<=len;++i)
c[rank[i]]++;
for(int i=1;i<=m;++i)
c[i]+=c[i-1];
for(int i=len;i>=1;--i)
sa[c[rank[top[i]]]--]=top[i];
}
void get_sa()
{
m=200;
for(int i=1;i<=len;++i)
{
rank[i]=s[i];
top[i]=i;
}
qsort();
for(int k=1,p=0;p<len;m=p,k<<=1)
{
p=0;
for(int i=1;i<=k;++i)
top[++p]=len-k+i;
for(int i=1;i<=len;++i)
if(sa[i]>k)
top[++p]=sa[i]-k;
qsort();
swap(top,rank);
rank[sa[1]]=p=1;
for(int i=2;i<=len;++i)
rank[sa[i]]=(top[sa[i-1]]==top[sa[i]]&&top[sa[i-1]+k]==top[sa[i]+k])?p:++p;
}
}
int solve(int x,int y)
{
int ret=0;
while(s[x++]==s[y++])
ret++;
return ret;
}
void get_height()
{
int cur=0;
for(int i=1;i<=len;++i)
{
if(cur!=0)
cur--;
height[rank[i]]=cur=cur+solve(i+cur,sa[rank[i]+1]+cur);
}
}
}suffix;
int main()
{
char s[maxn];
scanf("%s",s+1);
int n=strlen(s+1);
suffix.init(n,s);
suffix.clear();
suffix.get_sa();
for(int i=1;i<=n;++i)
printf("%d%c",suffix.sa[i],i==n?'\n':' ');
return 0;
}
2019中国大学生程序设计竞赛(CCPC) - 网络选拔赛 K-th occurrence HDU - 6704
题意:给定一个字符串,给定一个查询(l,r,k)求,s[l,r]这个子串第k次出现的首位置
思路:后缀数组+ST表+二分+主席树
每一个子串都是后缀的前缀,然后在后缀数组中,按字典序排序后,前缀相同的都是靠在一起的。假设这个子串的长度为len,那么和rank[l]的lcp大于等于len的,都是含有这个子串的。然后二分出上下区间就可以了。(一个固定的串与其他串的距离越远lcp越小,具有单调性)
找到了含有这个子串的区间后,我们要找第k个出现的首位置,也就是sa数组的第k小,对sa数组建立主席树,求第k小就可以了
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b) memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define isZero(d) (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;
struct RMQ
{
int dp[maxn][30];
void init(int len,int *a)
{
for(int i=1;i<=len;++i)
dp[i][0]=a[i];
for(int j=1;(1<<j)<=len;++j)
for(int i=1;i+(1<<j)-1<=len;++i)
dp[i][j]=min(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
}
int QueryMin(int l,int r)
{
int k=log2(r-l+1);
return min(dp[l][k],dp[r-(1<<k)+1][k]);
}
}rmq;
struct suffixvec
{
int c[maxn],sa[maxn],rank[maxn],height[maxn],x[maxn],y[maxn];
int m,n;
char s[maxn];
void init(int len1,char *s1)
{
n=len1;
strcpy(s+1,s1+1);
}
void clear()
{
for(int i=1;i<=n;++i)
sa[i]=rank[i]=height[i]=0;
}
void qsort()
{
for(int i=0;i<=m;++i)
c[i]=0;
for(int i=1;i<=n;++i)
c[rank[i]]++;
for(int i=1;i<=m;++i)
c[i]+=c[i-1];
for(int i=n;i>=1;--i)
sa[c[rank[y[i]]]--]=y[i];
}
void get_sa()
{
m=128;
for(int i=1;i<=n;++i)
{
rank[i]=s[i];
y[i]=i;
}
qsort();
for(int k=1;k<=n;k<<=1)
{
int p=0;
for(int i=1;i<=k;++i)
y[++p]=n-k+i;
for(int i=1;i<=n;++i)
if(sa[i]>k)
y[++p]=sa[i]-k;
qsort();
swap(y,rank);
rank[sa[1]]=p=1;
for(int i=2;i<=n;++i)
rank[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?p:++p;
if(p>=n)
break;
m=p;
}
}
void get_height()
{
for(int i=1;i<=n;++i)
rank[sa[i]]=i;
int k=0;
for(int i=1;i<=n;++i)
{
if(k)
k--;
int j=sa[rank[i]-1];
while(s[j+k]==s[i+k])
k++;
height[rank[i]]=k;
}
return;
}
int lcp(int x,int y)
{
int l=rank[x],r=rank[y];
if(l>r)
swap(l,r);
if(l==r)
return n-x+1;
return rmq.QueryMin(l+1,r);
}
}suffix;
int t,n,q;
char s[maxn];
int version[maxn],ST[maxn*40],ls[maxn*40],rs[maxn*40],no;
int Build(int L,int R)
{
int rt=++no;
if(L==R)
return rt;
int mid=(L+R)>>1;
Build(L,mid);
Build(mid+1,R);
return rt;
}
int Update(int pre,int p,int L,int R)
{
int rt=++no;
ls[rt]=ls[pre];
rs[rt]=rs[pre];
ST[rt]=ST[pre]+1;
if(L==R)
return rt;
int mid=(L+R)>>1;
if(p<=mid)
ls[rt]=Update(ls[pre],p,L,mid);
if(p>mid)
rs[rt]=Update(rs[pre],p,mid+1,R);
return rt;
}
int Query(int pre,int now,int k,int L,int R)
{
if(L==R)
return L;
int mid=(L+R)>>1;
int x=ST[ls[now]]-ST[ls[pre]];
if(k<=x)
return Query(ls[pre],ls[now],k,L,mid);
else
return Query(rs[pre],rs[now],k-x,mid+1,R);
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&q);
scanf("%s",s+1);
suffix.init(n,s);
suffix.clear();
suffix.get_sa();
suffix.get_height();
rmq.init(n,suffix.height);
no=0;
version[0]=Build(1,n);
for(int i=1;i<=n;++i)
version[i]=Update(version[i-1],suffix.sa[i],1,n);
for(int i=1;i<=q;++i)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
int L=1,R=suffix.rank[l],length=r-l+1;
int ansl=suffix.rank[l],ansr=suffix.rank[l];
while(L<R)
{
int mid=(L+R)>>1;
if(suffix.lcp(suffix.sa[mid],l)>=length)
R=mid;
else
L=mid+1;
}
ansl=L;
L=suffix.rank[l],R=n;
while(L<R)
{
int mid=(L+R+1)>>1;
if(suffix.lcp(suffix.sa[mid],l)>=length)
L=mid;
else
R=mid-1;
}
ansr=L;
//printf("l=%d r=%d\n",ansl,ansr);
if(ansr-ansl+1<k)
printf("-1\n");
else
printf("%d\n",Query(version[ansl-1],version[ansr],k,1,n));
}
}
return 0;
}
Boring counting HDU - 3518
题意:求字符串中子串出现两次的以上,且不重叠的子串个数
思路:枚举长度,遍历height数组,如果lcp的长度大于枚举长度,说明这个子串是存在的。当不满足了,那就可以枚举长度相同的下一种子串了。每一种子串在枚举的过程中,记录好该种子串的最左和最右端点。满足r-l>=len的。则答案+1
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b) memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define isZero(d) (abs(d) < 1e-8)
using namespace std;
const int maxn=2010,INF=0x3f3f3f3f;
const int mod=1e9+7;
struct RMQ
{
int dp[maxn][30];
void init(int len,int *a)
{
for(int i=1;i<=len;++i)
dp[i][0]=a[i];
for(int j=1;(1<<j)<=len;++j)
for(int i=1;i+(1<<j)-1<=len;++i)
dp[i][j]=min(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
}
int QueryMin(int l,int r)
{
int k=log2(r-l+1);
return min(dp[l][k],dp[r-(1<<k)+1][k]);
}
}rmq;
struct suffixvec
{
int c[maxn],sa[maxn],rank[maxn],height[maxn],x[maxn],y[maxn];
int m,n;
char s[maxn];
void init(int len1,char *s1)
{
n=len1;
strcpy(s+1,s1+1);
}
void clear()
{
for(int i=1;i<=n;++i)
sa[i]=rank[i]=height[i]=0;
}
void get_sa()
{
m=128;
for(int i=0;i<=m;++i)
c[i]=0;
for(int i=1;i<=n;++i)
c[x[i]=s[i]]++;
for(int i=2;i<=m;++i)
c[i]+=c[i-1];
for(int i=n;i>=1;--i)
sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<=1)
{
for(int i=0;i<=m;++i)
y[i]=0;
int p=0;
for(int i=n-k+1;i<=n;++i)
y[++p]=i;
for(int i=1;i<=n;++i)
if(sa[i]>k)
y[++p]=sa[i]-k;
for(int i=0;i<=m;++i)
c[i]=0;
for(int i=1;i<=n;++i)
c[x[y[i]]]++;
for(int i=1;i<=m;++i)
c[i]+=c[i-1];
for(int i=n;i>=1;--i)
sa[c[x[y[i]]]--]=y[i];
swap(x,y);
x[sa[1]]=1;
p=1;
for(int i=2;i<=n;++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
if(p==n)
break;
m=p;
}
}
void get_height()
{
for(int i=1;i<=n;++i)
rank[sa[i]]=i;
int k=0;
for(int i=1;i<=n;++i)
{
if(k)
k--;
int j=sa[rank[i]-1];
while(s[j+k]==s[i+k])
k++;
height[rank[i]]=k;
}
return;
}
int lcp(int x,int y)
{
int l=rank[x],r=rank[y];
if(l>r)
swap(l,r);
if(l==r)
return n-x+1;
return rmq.QueryMin(l+1,r);
}
}suffix;
char ss[maxn];
int main()
{
while(scanf("%s",ss+1)&&ss[1]!='#')
{
int n=strlen(ss+1);
suffix.init(n,ss);
suffix.clear();
suffix.get_sa();
suffix.get_height();
int ans=0;
for(int len=1;len<=n/2;++len)
{
int maxx=suffix.sa[1],minn=suffix.sa[1];
for(int i=2;i<=n;++i)
{
if(suffix.height[i]>=len)
{
maxx=max(maxx,suffix.sa[i]);
minn=min(minn,suffix.sa[i]);
}
else
{
if(maxx-minn>=len)
ans++;
maxx=suffix.sa[i];
minn=suffix.sa[i];
}
}
if(maxx-minn>=len)
ans++;
}
printf("%d\n",ans);
}
return 0;
}
1、一个串中两个串的最大公共前缀是多少?
这不就是Height吗?用rmq预处理,再O(1)查询。
2、一个串中可重叠的重复最长子串是多长?
就是求任意两个后缀的最长公共前缀,而任意两个后缀的最长公共前缀都是Height 数组里某一段的最小值,那最长的就是Height中的最大值。
3、一个串种不可重叠的重复最长子串是多长?
先二分答案,转化成判别式的问题比较好处理。假设当前需要判别长度为k是否符合要求,只需把排序后的后缀分成若干组,其中每组的后缀之间的Height 值都不小于k,再判断其中有没有不重复的后缀,具体就是看最大的SA值和最小的SA值相差超不超过k,有一组超过的话k就是合法答案。
4、一个字符串不相等的子串的个数是多少?
每个子串一定是某个后缀的前缀,那么原问题等价于求所有后缀之间的不相同的前缀的个数。而且可以发现每一个后缀Suffix[SA[i]]的贡献是Len - SA[i] + 1,但是有子串算重复,重复的就是Heigh[i]个与前面相同的前缀,那么减去就可以了。最后,一个后缀Suffix[SA[i]]的贡献就是Len - SA[k] + 1 - Height[k]。
原文链接:https://blog.csdn.net/YxuanwKeith/article/details/50636898
随便贴几个板子
1、这个板子可以过Boring counting HDU - 3518,却过不了K-th occurrence HDU - 6704,不明白
struct suffixvec
{
int c[maxn],sa[maxn],rank[maxn],height[maxn],x[maxn],y[maxn];
int m,n;
char s[maxn];
void init(int len1,char *s1)
{
n=len1;
strcpy(s+1,s1+1);
}
void clear()
{
for(int i=1;i<=n;++i)
sa[i]=rank[i]=height[i]=0;
}
void get_sa()
{
m=128;
for(int i=0;i<=m;++i)
c[i]=0;
for(int i=1;i<=n;++i)
c[x[i]=s[i]]++;
for(int i=2;i<=m;++i)
c[i]+=c[i-1];
for(int i=n;i>=1;--i)
sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<=1)
{
for(int i=0;i<=m;++i)
y[i]=0;
int p=0;
for(int i=n-k+1;i<=n;++i)
y[++p]=i;
for(int i=1;i<=n;++i)
if(sa[i]>k)
y[++p]=sa[i]-k;
for(int i=0;i<=m;++i)
c[i]=0;
for(int i=1;i<=n;++i)
c[x[y[i]]]++;
for(int i=1;i<=m;++i)
c[i]+=c[i-1];
for(int i=n;i>=1;--i)
sa[c[x[y[i]]]--]=y[i];
swap(x,y);
x[sa[1]]=1;
p=1;
for(int i=2;i<=n;++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
if(p==n)
break;
m=p;
}
}
void get_height()
{
for(int i=1;i<=n;++i)
rank[sa[i]]=i;
int k=0;
for(int i=1;i<=n;++i)
{
if(k)
k--;
int j=sa[rank[i]-1];
while(s[j+k]==s[i+k])
k++;
height[rank[i]]=k;
}
return;
}
int lcp(int x,int y)
{
int l=rank[x],r=rank[y];
if(l>r)
swap(l,r);
if(l==r)
return n-x+1;
return rmq.QueryMin(l+1,r);
}
}suffix;
参考博客:http://www.mamicode.com/info-detail-2759502.html