模板
其中 build_sa,getheight, st_build, query的部分都是将原字符串经过最后一个字符为0的处理后的模板
倍增和D3C两个版本 倍增是 D3C接近
倍增版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=1200000; //字符总长度加上连接字符的个数
int n,num;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn],dp[maxn][25];
char str1[maxn];
int lens[maxn],belong[maxn];
int len;
int cmp(int *r,int a,int b,int k)
{
return r[a]==r[b]&&r[a+k]==r[b+k];
}
void build_sa(int n,int m)
{
int *x=t1,*y=t2;
for(int i=0;i<m;i++)c[i]=0;
for(int i=0;i<n;i++)c[x[i]=s[i]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
int p;
for(int k=1;k<=n;k<<=1,m=p)
{
p=0;
for(int i=n-k;i<n;i++)y[p++]=i;
for(int i=0;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=0;i<n;i++)c[x[y[i]]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(int i=1;i<n;i++)
{
x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
}
if(p>=n)break;
}
}
void getheight(int n)
{
int k=0;
for(int i=0;i<n;i++)rk[sa[i]]=i;
for(int i=0;i<n;i++)
{
if(k)k--;
int j=sa[rk[i]-1];
while(s[i+k]==s[j+k])
{
k++;
}
height[rk[i]]=k;
}
}
void st_build(int n)
{
for(int i = 0; i < n; i++)
{
dp[i][0] = height[i];
}
for(int j = 1; (1<<j) <= n; j++)
for(int i = 0; (i+(1<<j)-1) <= n; i++)
dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
}
int query(int i, int j)
{
int l = min(rk[i], rk[j]);
int r = max(rk[i], rk[j]);
++l;
int cnt = log2(r-l+1), len = 1<<cnt;
return min(dp[l][cnt], dp[r-len+1][cnt]);
}
int main()
{
build_sa(n+1,100500);
getheight(n+1);
st_build(n+1);
int l,r;
query(l,r); //l,r为下标
return 0;
}
D3C版本:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
#define ll long long
using namespace std;
const int maxn=3200000; // d3c要开 三倍的空间 还要额外加上连接字符的长度
int n,num;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn],dp[maxn][25];
char str1[maxn];
int lens[maxn],belong[maxn];
int len;
int wa[maxn],wb[maxn],wv[maxn],wws[maxn];
void sort(int *r,int *a,int *b,int n,int m)
{
int i;
for(i=0;i<n;i++) wv[i]=r[a[i]];
for(i=0;i<m;i++) wws[i]=0;
for(i=0;i<n;i++) wws[wv[i]]++;
for(i=1;i<m;i++) wws[i]+=wws[i-1];
for(i=n-1;i>=0;i--) b[--wws[wv[i]]]=a[i];
return;
}
int c0(int *r,int a,int b)
{
return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
}
int c12(int k,int *r,int a,int b)
{
if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];
}
void dc3(int *r,int *sa,int n,int m)
{
int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
r[n]=r[n+1]=0;
for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
sort(r+2,wa,wb,tbc,m);
sort(r+1,wb,wa,tbc,m);
sort(r,wa,wb,tbc,m);
for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
if(p<tbc) dc3(rn,san,tbc,p);
else for(i=0;i<tbc;i++) san[rn[i]]=i;
for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
if(n%3==1) wb[ta++]=n-1;
sort(r,wb,wa,ta,m);
for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
for(i=0,j=0,p=0;i<ta && j<tbc;p++)
sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
for(;i<ta;p++) sa[p]=wa[i++];
for(;j<tbc;p++) sa[p]=wb[j++];
return;
}
void getheight(int n)
{
int k=0;
for(int i=0;i<n;i++)rk[sa[i]]=i;
for(int i=0;i<n;i++)
{
if(k)k--;
int j=sa[rk[i]-1];
while(s[i+k]==s[j+k])
{
k++;
}
height[rk[i]]=k;
}
}
void st_build(int n)
{
for(int i = 0; i < n; i++)
{
dp[i][0] = height[i];
}
for(int j = 1; (1<<j) <= n; j++)
for(int i = 0; (i+(1<<j)-1) <= n; i++)
dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
}
int query(int i, int j)
{
int l = min(rk[i], rk[j]);
int r = max(rk[i], rk[j]);
++l;
int cnt = log2(r-l+1), len = 1<<cnt;
return min(dp[l][cnt], dp[r-len+1][cnt]);
}
int main()
{
dc3(s, sa, n+1,100500);
getheight(n+1);
st_build(n+1);
int l,r;
query(l,r);
return 0;
}
第一题:
POJ2774
求两个串的最长公共子串,将两个串拼接在一起,然后求height数组 遍历一遍height数组如果sa[i-1]和sa[i]同时属于两个串
则更新答案
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=807000;
int s[maxn];
int sa[maxn],t1[maxn],t2[maxn],c[maxn];
int n;
char str1[maxn];
int belong[maxn];
int le[4010];
int len;
int t;
int vis[4010];
int anspos;
int rk[maxn],height[maxn];
int lenn[maxn];
char str[4005][205];
int wa[maxn],wb[maxn],wsf[maxn],wv[maxn];
void build_sa(int m)
{
int *x=t1,*y=t2;
for(int i=0;i<m;i++)c[i]=0;
for(int i=0;i<=n;i++)c[x[i]=s[i]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n;i>=0;i--)sa[--c[x[i]]]=i;
for(int k=1;k<=n;k<<=1)
{
// cout<<1<<endl;
int p=0;
for(int i=n+1-k;i<=n;i++)y[p++]=i;
for(int i=0;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=0;i<=n;i++)c[x[y[i]]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1,x[sa[0]]=0;
for(int i=1;i<=n;i++)
x[sa[i]]=((y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+k]==y[sa[i-1]+k]))?p-1:p++;
if(p>n)break;
m=p;
}
}
/*
int cmp(int *r,int a,int b,int k)
{
return r[a]==r[b]&&r[a+k]==r[b+k];
}
void getsa(int *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0; i<m; i++) wsf[i]=0;
for(i=0; i<=n; i++) wsf[x[i]=r[i]]++;
for(i=1; i<m; i++) wsf[i]+=wsf[i-1];
for(i=n; i>=0; i--) sa[--wsf[x[i]]]=i;
p=1;
j=1;
for(; p<=n; j*=2,m=p)
{
for(p=0,i=n+1-j; i<=n; i++) y[p++]=i;
for(i=0; i<=n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0; i<=n; i++) wv[i]=x[y[i]];
for(i=0; i<m; i++) wsf[i]=0;
for(i=0; i<=n; i++) wsf[wv[i]]++;
for(i=1; i<m; i++) wsf[i]+=wsf[i-1];
for(i=n; i>=0; i--) sa[--wsf[wv[i]]]=y[i];
t=x;
x=y;
y=t;
x[sa[0]]=0;
for(p=1,i=1; i<=n; i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
*/
void getheight()
{
int k=0;
for(int i=0;i<=n;i++)rk[sa[i]]=i;
for(int i=0;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 check(int x,int n)
{
for(int i=1;i<=n-1;i++)
{
if(height[i]<x) continue;
int cnt=0;
for(int j=0;j<=t;j++) vis[j]=0;
while(height[i]>=x&&i<=n-1)
{
if(!vis[belong[sa[i-1]]])
{
vis[belong[sa[i-1]]]=1;
cnt++;
}
i++;
}
if(!vis[belong[sa[i-1]]])
{
vis[belong[sa[i-1]]]=1;
cnt++;
}
if(cnt>=t)
{
anspos=sa[i-1];
return true;
}
}
return false;
}
int main()
{
int len;
while(~scanf("%d",&t))
{
if(t==0) break;
n=0;
int pos=30;
for(int i=0;i<t;i++)
{
scanf("%s",str[i]);
lenn[i]=strlen(str[i]);
for(int j=0;j<lenn[i];j++)
{
s[n++]=str[i][j]-'a'+1;
belong[n-1]=i;
}
s[n++]=pos++;
}
s[n]=0;
build_sa(5000);
getheight();
int l=1,r=200,mid;
//for(int i=0;i<=n;i++)cout<<sa[i]<<endl;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid,n)) l=mid+1;
else r=mid-1;
}
if(r==0) printf("IDENTITY LOST\n");
else
{
//cout<<anspos<<endl;
for(int i=anspos;i<anspos+r;i++)
printf("%c",s[i]-1+'a');
printf("\n");
}
}
return 0;
}
第二题:
POJ3450
求多个串的最长公共子串,首先我们把所有字符串串在一起,求sa和height,然后我们对最长子串的答案进行二分,先预处理一下每个下标属于哪个字符串,如果有一段连续的height[i] 都大于等于mid 并且分别属于n个串,那么记录最后的anspos,从i=anspos到i<anspos+r 输出答案
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=807000;
int s[maxn];
int sa[maxn],t1[maxn],t2[maxn],c[maxn];
int n;
char str1[maxn];
int belong[maxn];
int le[4010];
int len;
int t;
int vis[4010];
int anspos;
int rk[maxn],height[maxn];
int lenn[maxn];
char str[4005][205];
int wa[maxn],wb[maxn],wsf[maxn],wv[maxn];
void build_sa(int m)
{
int *x=t1,*y=t2;
for(int i=0;i<m;i++)c[i]=0;
for(int i=0;i<=n;i++)c[x[i]=s[i]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n;i>=0;i--)sa[--c[x[i]]]=i;
for(int k=1;k<=n;k<<=1)
{
int p=0;
for(int i=n+1-k;i<=n;i++)y[p++]=i;
for(int i=0;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=0;i<=n;i++)c[x[y[i]]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1,x[sa[0]]=0;
for(int i=1;i<=n;i++)
x[sa[i]]=((y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+k]==y[sa[i-1]+k]))?p-1:p++;
if(p>n)break;
m=p;
}
}
void getheight()
{
int k=0;
for(int i=0;i<=n;i++)rk[sa[i]]=i;
for(int i=0;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 check(int x,int n)
{
for(int i=1;i<=n-1;i++)
{
if(height[i]<x) continue;
int cnt=0;
for(int j=0;j<=t;j++) vis[j]=0;
while(height[i]>=x&&i<=n-1)
{
if(!vis[belong[sa[i-1]]])
{
vis[belong[sa[i-1]]]=1;
cnt++;
}
i++;
}
if(!vis[belong[sa[i-1]]])
{
vis[belong[sa[i-1]]]=1;
cnt++;
}
if(cnt>=t)
{
anspos=sa[i-1];
return true;
}
}
return false;
}
int main()
{
int len;
while(~scanf("%d",&t))
{
if(t==0) break;
n=0;
int pos=30;
for(int i=0;i<t;i++)
{
scanf("%s",str[i]);
lenn[i]=strlen(str[i]);
for(int j=0;j<lenn[i];j++)
{
s[n++]=str[i][j]-'a'+1;
belong[n-1]=i;
}
s[n++]=pos++;
}
s[n]=0;
build_sa(5000);
getheight();
int l=1,r=200,mid;
//for(int i=0;i<=n;i++)cout<<sa[i]<<endl;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid,n)) l=mid+1;
else r=mid-1;
}
if(r==0) printf("IDENTITY LOST\n");
else
{
//cout<<anspos<<endl;
for(int i=anspos;i<anspos+r;i++)
printf("%c",s[i]-1+'a');
printf("\n");
}
}
return 0;
}
第三题:
POJ3294
题意和上面的类似,求多个字符串的最长公共子串,并且这个公共子串满足属于大于n/2 的字符串的子串,并且有多个答案的时候全部输出,与第二题的做法类似,只是check的时候改成>n/2 返回值为true, 存答案的时候我们用anspos[++xans]去存,然后当满足条件的时候我们用xans更新ans,这样就能保证每次能覆盖掉原先的anspos而最后一个答案的ans却不会被覆盖
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=200050;
int n,num;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn];
int belong[maxn],vis[maxn];
char str1[maxn];
int ans,anspos[maxn];
int xans;
int len;
int cmp(int *r,int a,int b,int k)
{
return r[a]==r[b]&&r[a+k]==r[b+k];
}
void build_sa(int m)
{
int *x=t1,*y=t2;
for(int i=0;i<m;i++)c[i]=0;
for(int i=0;i<=n;i++)c[x[i]=s[i]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n;i>=0;i--)sa[--c[x[i]]]=i;
int p;
for(int k=1;k<=n;k<<=1,m=p)
{
p=0;
for(int i=n+1-k;i<=n;i++)y[p++]=i;
for(int i=0;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=0;i<=n;i++)c[x[y[i]]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(int i=1;i<=n;i++)
{
x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
}
if(p>n)break;
}
}
void getheight()
{
int k=0;
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=0;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 check(int x)
{
xans=0;
int flag=0;
for(int i=1;i<=n-1;i++)
{
int cnt=0;
if(height[i]<x)continue;
for(int j=0;j<=num;j++)vis[j]=0;
while(height[i]>=x&&i<=n-1)
{
if(!vis[belong[sa[i-1]]])
{
vis[belong[sa[i-1]]]=1;
cnt++;
}
i++;
}
if(!vis[belong[sa[i-1]]])
{
vis[belong[sa[i-1]]]=1;
cnt++;
}
if(cnt>num/2)
{
anspos[++xans]=sa[i-1];
flag=1;
cnt=0;
}
}
if(flag)
{
ans=xans;
return true;
}
return false;
}
int main()
{
scanf("%d",&num);
while(1)
{
ans=0;
n=0;
if(num==0)break;
int pos=60;
for(int i=1;i<=num;i++)
{
scanf("%s",str1);
len=strlen(str1);
for(int j=0;j<len;j++)
{
s[n++]=str1[j]-'a'+1;
belong[n-1]=i;
}
s[n++]=pos++;
}
s[n]=0;
build_sa(500);
getheight();
int l=1,r=1000,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid))l=mid+1;
else r=mid-1;
}
if(r==0)
{
printf("?\n");
}
else
{
for(int j=1;j<=ans;j++)
{
for(int i=anspos[j];i<anspos[j]+r;i++)
{
printf("%c",s[i]+'a'-1);
}
printf("\n");
}
}
scanf("%d",&num);
if(num!=0)printf("\n");
else break;
}
}
第四题:
给两个不同的字符串A,B求长度不小于K的的公共子串的个数(可以相同) POJ3415
这题主要是利用sa数组和height数组的性质
1.固定一个子串i,对于和他排名相邻的串的lcp一定比和他排名相差更大的子串的lcp要大。
2.height数组的作用是把两个排名相邻的子串分在一起。
3.对于height数组逐渐递增的时候,排名后面的串与排名前面的串的lcp等于height数组的大小
例如如果前面是
1 2 3 4 5 1
BB BB BA AA AB BA
则对于3A来说 他和前面三个B的lcp 为height 数组的前缀和1+2+3;
对于 4A来说他和前面三个B的lcp也为height数组的前缀和1+2+3;
也就是说我们可以固定 当sa[i-1]为B时统计贡献sa[i]为A时累计答案。这样不会重复只会计算下划线的贡献
由于height数组只有递增的时候才有这个性质所以我们可以对height数组建立一个单调栈,当第六个BA为1
的时候我们把前面的2,3,4,5都出栈由于4,5没有产生贡献所以其数量统计num=0,我们一边出栈一边统计贡献的数量,并且不断更新前缀和pre,
pre-=3, pre+=1;
pre-=2,pre+=1;
把2,3出栈后num累计为2,然后由于第6个height组合的sa[i-1]为B 会产生贡献所以入栈时,
cnt[top]=num+1;
pre+=1;
然后当sa[i]为A的时候我们累计答案,pre=1+1+1+1, ans+=pre;
也就是说我们只要维护一个height数组的递增单调栈,一个贡献数量数组cnt ,一个贡献前缀和pre 就可以求出所有以sa[i]为A的贡献和
但是 有可能为
BB BB BA AA AA AB
这样我们会漏掉最后的B和前面的A 的lcp 所以我们对sa[i-1]=B sa[i]=A用单调栈求一遍lcp和,再对sa[i-1]=A,sa[i]=B再用单调栈求一遍lcp和 ,保证不重不漏, 复杂度为O(len(str1)+len(str2))
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=300050;
int n,len;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn];
int belong[maxn],vis[maxn];
char str1[maxn],str2[maxn];
int cnt[maxn];
int sta[maxn];
int cmp(int *r,int a,int b,int k)
{
return r[a]==r[b]&&r[a+k]==r[b+k];
}
void build_sa(int m)
{
int *x=t1,*y=t2;
for(int i=0;i<m;i++)c[i]=0;
for(int i=0;i<=n;i++)c[x[i]=s[i]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n;i>=0;i--)sa[--c[x[i]]]=i;
int p;
for(int k=1;k<=n;k<<=1,m=p)
{
p=0;
for(int i=n+1-k;i<=n;i++)y[p++]=i;
for(int i=0;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=0;i<=n;i++)c[x[y[i]]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(int i=1;i<=n;i++)
{
x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
}
if(p>n)break;
}
}
void getheight()
{
int k=0;
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=0;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()
{
int k;
while(scanf("%d",&k)==1)
{
if(k==0)break;
n=0;
scanf("%s",str1);
len=strlen(str1);
for(int i=0;i<len;i++)
{
s[n++]=str1[i]-'A'+1;
}
s[n++]=100;
scanf("%s",str2);
len=strlen(str2);
for(int i=0;i<len;i++)
{
s[n++]=str2[i]-'A'+1;
}
s[n]=0;
build_sa(300);
getheight();
len=strlen(str1);
ll pre=0;
ll ans=0;
int top=0;
for(int i=2;i<=n-1;i++)
{
if(height[i]<k)
{
top=0;
pre=0;
}
else
{
int num=0; //统计栈顶元素的数量
while(top&&sta[top]>height[i])
{
pre-=1ll*cnt[top]*(sta[top]-k+1);
pre+=1ll*cnt[top]*(height[i]-k+1);
num+=cnt[top];
top--;
}
sta[++top]=height[i];
if(sa[i-1]>len)
{
pre+=1ll*(height[i]-k+1);
cnt[top]=num+1;
}
else cnt[top]=num;
if(sa[i]<len)
{
ans+=pre;
}
}
}
pre=0;
top=0;
for(int i=2;i<=n-1;i++)
{
if(height[i]<k)
{
top=0;
pre=0;
}
else
{
int num=0; //统计栈顶元素的数量
while(top&&sta[top]>height[i])
{
pre-=1ll*cnt[top]*(sta[top]-k+1);
pre+=1ll*cnt[top]*(height[i]-k+1);
num+=cnt[top];
top--;
}
sta[++top]=height[i];
if(sa[i-1]<len)
{
pre+=1ll*(height[i]-k+1);
cnt[top]=num+1;
}
else cnt[top]=num;
if(sa[i]>len)
{
ans+=pre;
}
}
}
cout<<ans<<endl;
}
return 0;
}
第五题:
求一个串的最长回文子串
把这个串的正向和逆向拼在一起,求sa和height以后,遍历height数组如果sa[i-1]和sa[i]分别属于两个子串,并且满足
sa[i-1]和sa[i]所代表的串在原字符串的同一位置则更新答案(两个串分别加上原字符串开头后长度相等)则更新答案,如果有长度相等的则保留最小下标。
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=200050;
int n,num;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn];
int belong[maxn],vis[maxn];
char str1[maxn],str2[maxn];
int len;
int cmp(int *r,int a,int b,int k)
{
return r[a]==r[b]&&r[a+k]==r[b+k];
}
void build_sa(int m)
{
int *x=t1,*y=t2;
for(int i=0;i<m;i++)c[i]=0;
for(int i=0;i<=n;i++)c[x[i]=s[i]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n;i>=0;i--)sa[--c[x[i]]]=i;
int p;
for(int k=1;k<=n;k<<=1,m=p)
{
p=0;
for(int i=n+1-k;i<=n;i++)y[p++]=i;
for(int i=0;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=0;i<=n;i++)c[x[y[i]]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(int i=1;i<=n;i++)
{
x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
}
if(p>n)break;
}
}
void getheight()
{
int k=0;
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=0;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()
{
while(~scanf("%s",str1))
{
len=strlen(str1);
n=0;
for(int i=0;i<len;i++)
{
s[n++]=str1[i]-'A'+1;
}
s[n++]=100;
for(int i=len-1;i>=0;i--)
{
s[n++]=str1[i]-'A'+1;
}
s[n]=0;
build_sa(300);
getheight();
int ans=1;
int anspos=0;
for(int i=2;i<=n-1;i++)
{
int mi=min(sa[i-1],sa[i]);
int mx=max(sa[i-1],sa[i]);
if(mi>len||mx<len)continue;
if(mi+height[i]!=n-mx)continue;
if(height[i]>ans)
{
ans=height[i];
anspos=mi;
}
else if(height[i]==ans)
{
anspos=min(anspos,mi);
}
}
for(int i=anspos,j=1;j<=ans;i++,j++)
{
printf("%c",s[i]+'A'-1);
}
printf("\n");
}
return 0;
}
第六题:
题意给你一个字符串,然后求一个出现大于等于k次并且最长的可重叠子串
长度我们可以二分答案,check的时候只要连续有k-1个height数组长度大于等于mid 就更新答案。
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=2010150;
int n,num;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn];
int belong[maxn],vis[maxn];
char str1[maxn];
int len;
int k;
int cmp(int *r,int a,int b,int k)
{
return r[a]==r[b]&&r[a+k]==r[b+k];
}
void build_sa(int m)
{
int *x=t1,*y=t2;
for(int i=0;i<m;i++)c[i]=0;
for(int i=0;i<=n;i++)c[x[i]=s[i]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n;i>=0;i--)sa[--c[x[i]]]=i;
int p;
for(int k=1;k<=n;k<<=1,m=p)
{
p=0;
for(int i=n+1-k;i<=n;i++)y[p++]=i;
for(int i=0;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=0;i<=n;i++)c[x[y[i]]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(int i=1;i<=n;i++)
{
x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
}
if(p>n)break;
}
}
void getheight()
{
int k=0;
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=0;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 check(int x)
{
int cnt=1;
for(int i=2;i<=n;i++)
{
if(height[i]>=x)
{
cnt++;
}
else cnt=1;
if(cnt==k)return true;
}
return false;
}
int main()
{
while(scanf("%d%d",&num,&k)!=EOF)
{
int x;
n=0;
for(int i=1;i<=num;i++)
{
scanf("%d",&x);
s[n++]=x;
}
s[n]=0;
build_sa(1000110);
getheight();
int l,r,mid;
l=0,r=n;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid))l=mid+1;
else r=mid-1;
}
cout<<r<<endl;
}
}
第七题
给你n个字符串m个询问,每次询问两个字符串的lcp为多少
先把字符串拼接在一起然后用st表预处理出任意两个下标的lcp,预处理求出每个字符串的开头下标每次询问,O(1)查询两个字符串开头下标的lcp就行了。
貌似D3C并没有比倍增 快很多
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=1200000; //字符总长度加上连接字符的个数
int n,num;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn],dp[maxn][25];
char str1[maxn];
int lens[maxn],belong[maxn];
int len;
int cmp(int *r,int a,int b,int k)
{
return r[a]==r[b]&&r[a+k]==r[b+k];
}
void build_sa(int n,int m)
{
int *x=t1,*y=t2;
for(int i=0;i<m;i++)c[i]=0;
for(int i=0;i<n;i++)c[x[i]=s[i]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
int p;
for(int k=1;k<=n;k<<=1,m=p)
{
p=0;
for(int i=n-k;i<n;i++)y[p++]=i;
for(int i=0;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=0;i<n;i++)c[x[y[i]]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(int i=1;i<n;i++)
{
x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
}
if(p>=n)break;
}
}
void getheight(int n)
{
int k=0;
for(int i=0;i<n;i++)rk[sa[i]]=i;
for(int i=0;i<n;i++)
{
if(k)k--;
int j=sa[rk[i]-1];
while(s[i+k]==s[j+k])
{
k++;
}
height[rk[i]]=k;
}
}
void st_build(int n)
{
for(int i = 0; i < n; i++)
{
dp[i][0] = height[i];
}
for(int j = 1; (1<<j) <= n; j++)
for(int i = 0; (i+(1<<j)-1) <= n; i++)
dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
}
int query(int i, int j)
{
int l = min(rk[i], rk[j]);
int r = max(rk[i], rk[j]);
++l;
int cnt = log2(r-l+1), len = 1<<cnt;
return min(dp[l][cnt], dp[r-len+1][cnt]);
}
int main()
{
int t;
scanf("%d",&t);
int cont=0;
while(t--)
{
scanf("%d",&num);
n=0;
int pos=50;
for(int i=1;i<=num;i++)
{
scanf("%s",str1);
len=strlen(str1);
lens[i]=len;
for(int j=0;j<len;j++)
{
s[n++]=str1[j]-'a'+1;
}
belong[i]=n-len;
s[n++]=pos++;
}
s[n]=0;
build_sa(n+1,100500);
getheight(n+1);
st_build(n+1);
int q;
int u,v;
scanf("%d",&q);
printf("Case %d:\n",++cont);
for(int i=1;i<=q;i++)
{
scanf("%d%d",&u,&v);
if(u==v)printf("%d\n",lens[u]);
else printf("%d\n",query(belong[u],belong[v]));
}
}
}