hihocoder 1403 1407 1415 后缀数组*3 最长公共子串/最长不重叠重复出现的子串/最长k次重复子串
hihocoder 1403:
求一个字符串中至少出现了K次的最长可重叠子串
做法:预处理出height,每次用单调队列线性求区间最小值即可
#include<bits/stdc++.h>
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define per(ii,a,b) for(int ii=b;ii>=a;--ii)
using namespace std;//head
const int maxn=4e4+10,maxm=2e6+10;
int casn,n,m,k;
char a[maxn];
class suffix{public:
int sa[maxn],rank[maxn],h[maxn],wa[maxn],wb[maxn],wc[maxn],wd[maxn];
char *str;
void geth(int n){
int i,j,k=0;
for(i=1;i<=n;++i) rank[sa[i]]=i;
for(i=0;i<n;h[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];str[i+k]==str[j+k];++k);
}
void getsa(char *_s,int n,int m){
str=_s;
int i,j,p,*x=wa,*y=wb;
rep(i,0,m-1) wd[i]=0;
rep(i,0,n-1) wd[x[i]=str[i]]++;
rep(i,1,m-1) wd[i]+=wd[i-1];
per(i,0,n-1) sa[--wd[x[i]]]=i;
for(j=1,p=1;p<n;j*=2,m=p){
for(p=0,i=n-j;i<n;++i) y[p++]=i;
rep(i,0,n-1) if(sa[i]>=j) y[p++]=sa[i]-j;
rep(i,0,n-1) wc[i]=x[y[i]];
rep(i,0,m-1) wd[i]=0;
rep(i,0,n-1) wd[wc[i]]++;
rep(i,1,m-1) wd[i]+=wd[i-1];
per(i,0,n-1) sa[--wd[wc[i]]]=y[i];
for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;++i)
x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
}
geth(n-1);
}
int solve(int n,int k){
deque<int>que;
int ans=0;
rep(i,1,n){
while(!que.empty()&&h[i]<h[que.back()]) que.pop_back();
que.push_back(i);
while(!que.empty()&&i-que.front()>=k) que.pop_front();
assert(!que.empty());
if(!que.empty())ans=max(ans,h[que.front()]);
}
return ans;
}
}suf;
int main() {
cin>>n>>k;
rep(i,0,n-1) {
int x;cin>>x;
a[i]=x;
}
a[n]=0;
k--;
suf.getsa(a,n+1,233);
cout<<suf.solve(n,k);
return 0;
}
hihocoder1407:
求字符串中最长的至少不重叠地出现了2次的子串
做法:预处理出height,二分答案,每次检查每一个连续的height<mid的区间内,最大和最小的后缀距离是否>=mid即可(我换了板子)
#include<bits/stdc++.h>
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define per(ii,a,b) for(int ii=b;ii>=a;--ii)
using namespace std;//head
const int maxn=100000+10,maxm=2e6+10;
int casn,n,m,k;
int a[maxn];
namespace suffix{
int tr[maxn],rank[maxn],sa[maxn],h[maxn];
int has[maxn],bir[maxn],val[maxn],deep[maxn];
int cmp(int x,int y,int k){
if(x+k>n||y+k>n)return 0;
return rank[x]==rank[y]&&rank[x+k]==rank[y+k];
}
void getsa(int *s,int n,int m=maxn-5){
int i,cnt;
for(i=1;i<=n;i++)has[s[i]]++;
for(i=1,cnt=0;i<=m;i++)if(has[i])tr[i]=++cnt;
for(i=1;i<=m;i++)has[i]+=has[i-1];
for(i=1;i<=n;i++)rank[i]=tr[s[i]],sa[has[s[i]]--]=i;
for(int k=1;cnt!=n;k<<=1){
for(i=1;i<=n;i++)has[i]=0;
for(i=1;i<=n;i++)has[rank[i]]++;
for(i=1;i<=n;i++)has[i]+=has[i-1];
for(i=n;i>=1;i--)if(sa[i]>k)tr[sa[i]-k]=has[rank[sa[i]-k]]--;
for(i=1;i<=k;i++)tr[n-i+1]=has[rank[n-i+1]]--;
for(i=1;i<=n;i++)sa[tr[i]]=i;
for(i=1,cnt=0;i<=n;i++)tr[sa[i]]=cmp(sa[i],sa[i-1],k) ? cnt:++cnt;
for(i=1;i<=n;i++)rank[i]=tr[i];
}
for(int i=1;i<=n;i++){
if(rank[i]==1)continue;
for(int j=max(1,h[rank[i-1]]-1);;j++){
if(s[i+j-1]==s[sa[rank[i]-1]+j-1])h[rank[i]]=j;
else break;
}
}
}
bool check(int mid,int n){
int mx=0,mn=n;
rep(i,1,n){
if(h[i]<mid){
if(mx&&mx-mn>=mid) return true;
mx=mn=sa[i];
}else mx=max(mx,sa[i]),mn=min(mn,sa[i]);
}
if(mx&&mx-mn>=mid) return true;
return false;
}
}
int main() {
cin>>n;
rep(i,1,n) cin>>a[i];
suffix::getsa(a,n,1010);
int l=0,r=n;
int ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(suffix::check(mid,n)) ans=mid,l=mid+1;
else r=mid-1;
}
cout<<ans;
return 0;
}
hihocoder 1415
求两个子串的最长公共子串
做法:拼接两个子串,中间隔着一个从没出现过的字符(我选择的是比所有字符都大的一个字符),
然后计算出height数组,接着对于每一对相邻的后缀,如果分别属于两个字符串,那么ans和他们两个的lcp取一下最大值就行
#include<bits/stdc++.h>
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define per(ii,a,b) for(int ii=b;ii>=a;--ii)
using namespace std;//head
const int maxn=100000+10,maxm=2e6+10;
int casn,k;
namespace suffix{
int tr[maxn],rank[maxn],sa[maxn],h[maxn];
int has[maxn],bir[maxn],val[maxn],deep[maxn];
int n;
int cmp(int x,int y,int k){
if(x+k>n||y+k>n)return 0;
return rank[x]==rank[y]&&rank[x+k]==rank[y+k];
}
void getsa(int *s,int _n,int m=maxn-5){
int i,cnt;n=_n;
for(i=1;i<=n;i++)has[s[i]]++;
for(i=1,cnt=0;i<=m;i++)if(has[i])tr[i]=++cnt;
for(i=1;i<=m;i++)has[i]+=has[i-1];
for(i=1;i<=n;i++)rank[i]=tr[s[i]],sa[has[s[i]]--]=i;
for(int k=1;cnt!=n;k<<=1){
for(i=1;i<=n;i++)has[i]=0;
for(i=1;i<=n;i++)has[rank[i]]++;
for(i=1;i<=n;i++)has[i]+=has[i-1];
for(i=n;i>=1;i--)if(sa[i]>k)tr[sa[i]-k]=has[rank[sa[i]-k]]--;
for(i=1;i<=k;i++)tr[n-i+1]=has[rank[n-i+1]]--;
for(i=1;i<=n;i++)sa[tr[i]]=i;
for(i=1,cnt=0;i<=n;i++)tr[sa[i]]=cmp(sa[i],sa[i-1],k) ? cnt:++cnt;
for(i=1;i<=n;i++)rank[i]=tr[i];
}
for(int i=1;i<=n;i++){
if(rank[i]==1)continue;
for(int j=max(1,h[rank[i-1]]-1);;j++){
if(s[i+j-1]==s[sa[rank[i]-1]+j-1])h[rank[i]]=j;
else break;
}
}
}
int solve(int len,int n){
int ans=0;
rep(i,1,len) if((sa[i]<=n)^(sa[i-1]<=n))
ans=max(ans,h[i]);
return ans;
}
}
char a[maxn],b[maxn];
int c[maxn<<1];
int main() {
cin>>(a+1)>>(b+1);
int n=strlen(a+1),m=strlen(b+1);
rep(i,1,n) c[i]=a[i];c[n+1]='z'+1;
rep(i,1,m) c[i+n+1]=b[i];
int len=n+m+1;
suffix::getsa(c,len,233);
cout<<suffix::solve(len,n);
return 0;
}

浙公网安备 33010602011771号