SA练习题总结-篇一
SA练习题总结-篇一
总算是把\(SA\)相关的入门练习题做完了,也算是记下学习笔记,加深印象。
A - Musical Theme
给定长度为\(n\)的数组,询问不重复的相似序列(序列任意相邻两项差相等)的最长长度。
换个说法就是,求最长的不重复且至少出现两次的子串。
思路:
这个题没什么好讲的,直接差分完求出\(SA\)数组。然后二分长度,利用\(height\)数组将之分组(这也是处理不重复的常规套路),这样保证每个组的\(lcp\)都\(\geq\)当前的二分长度。
最后注意,因为是差分,所以直接把长度加1.
//#pragma GCC optimize(2)
//#pragma GCC optimize(3, "Ofast", "inline")
//#include <bits/stdc++.h>
#include "iostream"
#include "stdio.h"
#include "string.h"
#include "algorithm"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int N=1e6+5;
const ll mod=998244353;
const double eps=1e-5;
//const double pi=acos(-1);
#define ls p<<1
#define rs p<<1|1
int s[N],a[N];
int n,rk[N],sa[N],tp[N],m,tax[N];
int height[N];
void Rsort()
{
for(int i=0;i<=m;i++) tax[i]=0;
for(int i=1;i<=n;i++) tax[rk[i]]++;
for(int i=1;i<=m;i++) tax[i]+=tax[i-1];
for(int i=n;i;i--) sa[tax[rk[tp[i]]]--]=tp[i];
}
void suffix()
{
m=300;
for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i;
Rsort();
for(int w=1,p=0;p<n;w<<=1,m=p)
{
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;
Rsort();
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);
}
}
int 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 check(int k)
{
int mx=0,mn=0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
if(height[i]<k)
mx=mx=sa[i];
else
{
mx=max(mx,max(sa[i],sa[i-1]));
mn=min(mn,min(sa[i],sa[i-1]));
if(mx-mn>k) return true;
}
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
while(scanf("%d",&n)&&n)
{
for(int i=1;i<=n;i++) scanf("%d",&s[i]);
for(int i=n;i>=2;i--) s[i]-=s[i-1]-88;
s[1]=88;
suffix();
int l=0,r=n;
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l>=4?l+1:0);
}
return 0;
}
B - Milk Patterns
跟上个题差不多,不过是求至少出现\(k\)次的子串,同样要求不重复。
思路:
同上。
//#pragma GCC optimize(2)
//#pragma GCC optimize(3, "Ofast", "inline")
//#include <bits/stdc++.h>
#include "iostream"
#include "stdio.h"
#include "string.h"
#include "algorithm"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int N=1e6+5;
const ll mod=998244353;
const double eps=1e-5;
//const double pi=acos(-1);
#define ls p<<1
#define rs p<<1|1
int s[N],a[N];
int n,rk[N],sa[N],tp[N],m,tax[N];
int height[N];
void Rsort()
{
for(int i=0;i<=m;i++) tax[i]=0;
for(int i=1;i<=n;i++) tax[rk[i]]++;
for(int i=1;i<=m;i++) tax[i]+=tax[i-1];
for(int i=n;i;i--) sa[tax[rk[tp[i]]]--]=tp[i];
}
void suffix()
{
m=1e6;
for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i;
Rsort();
for(int w=1,p=0;p<n;w<<=1,m=p)
{
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;
Rsort();
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);
}
}
int 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 check(int l)
{
int cnt=1,ans=0;
for(int i=1;i<=n;i++)
{
if(height[i]<l)
{
ans=max(ans,cnt);
cnt=1;
}
else cnt++;
}
return max(ans,cnt);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
int k;
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>s[i];
suffix();
int l=0,r=n;
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid)>=k) l=mid;
else r=mid-1;
}
printf("%d\n",l);
return 0;
}
C - Distinct Substrings
这个题问的是本质不同的子串的个数。
思路:
设\(n\)为字符串的长度,那么所有子串的个数为\(\frac{n*(n+1)}{2}\)。
然后只用减去重复的个数即可,这里也就是\(\sum height[i]\),\(height[i]\)为后缀\(sa[i]\)与\(sa[i-1]\)的\(lcp\)。
//#pragma GCC optimize(2)
//#pragma GCC optimize(3, "Ofast", "inline")
//#include <bits/stdc++.h>
#include "iostream"
#include "stdio.h"
#include "string.h"
#include "algorithm"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int N=1e6+5;
const ll mod=998244353;
const double eps=1e-5;
//const double pi=acos(-1);
#define ls p<<1
#define rs p<<1|1
char s[N];
int n,rk[N],sa[N],tp[N],m,tax[N];
int height[N];
void Rsort()
{
for(int i=0;i<=m;i++) tax[i]=0;
for(int i=1;i<=n;i++) tax[rk[i]]++;
for(int i=1;i<=m;i++) tax[i]+=tax[i-1];
for(int i=n;i;i--) sa[tax[rk[tp[i]]]--]=tp[i];
}
void suffix()
{
m=60;
for(int i=1;i<=n;i++) rk[i]=s[i]-'A'+1,tp[i]=i;
Rsort();
for(int w=1,p=0;p<n;w<<=1,m=p)
{
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;
Rsort();
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);
}
}
int 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 check(int l)
{
int cnt=1,ans=0;
for(int i=1;i<=n;i++)
{
if(height[i]<l)
{
ans=max(ans,cnt);
cnt=1;
}
else cnt++;
}
return max(ans,cnt);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
int _;
cin>>_;
while(_--)
{
cin>>(s+1);
n=strlen(s+1);
suffix();
int ans=n*(n+1)/2;
for(int i=1;i<=n;i++)
ans-=height[i];
cout<<ans<<endl;
}
return 0;
}