SA练习题总结-篇一

SA练习题总结-篇一

专题链接

总算是把\(SA\)相关的入门练习题做完了,也算是记下学习笔记,加深印象。

A - Musical Theme

POJ-1743

给定长度为\(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

POJ-3261

跟上个题差不多,不过是求至少出现\(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

SPOJ-DISUBSTR

这个题问的是本质不同的子串的个数。

思路:

\(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;
}
posted @ 2020-04-19 12:08  Suiyue_Li  阅读(184)  评论(0编辑  收藏  举报