字符串-后缀数组

倍增法,每次排2^j长度的段,转移就是双关键字排序就好啦!

求height可以利用height[rank[i]]>=height[rank[i-1]]-1的性质,当然证明考虑构造,并反证,假设在其中插入元素使性质不成立,推矛盾就可以了。

基本上是从网上抄来的模板啦,解释一下代码吧~

x在交换之前充当了rank的功能。

关于c的用处,来看计数排序的实现方法:

for(i=1;i<=n;i++){
    scanf("%d",&a[i]);
    ++c[a[i]];
}
for(i=1;i<=m;i++)
    c[i]+=c[i-1];
for(i=n;i;i--)
    p[c[a[i]]--]=i;
for(i=1;i<=n;i++)
    printf("%d",a[p[i]]);

y是按后半部分排序后前半部分开头序号,即x[y[i]+j]是单调的。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e6+6;

char s[N];
int t1[N],t2[N],c[N];
int sa[N],rk[N],ht[N];

void gtsa(int n,int m){
    int i,j,p=0,*x=t1,*y=t2;
    for(i=1;i<=n;i++)
        ++c[x[i]=s[i]];
    for(i=1;i<=m;i++)
        c[i]+=c[i-1];
    for(i=n;i;i--)
        sa[c[x[i]]--]=i;
    for(i=1;i<=m;i++)
        c[i]=0;
    for(j=1;j<=n&&p<n;j<<=1){
        p=0;
        for(i=n-j+1;i<=n;i++)
            y[++p]=i;
        for(i=1;i<=n;i++)
            if(sa[i]>j)
                y[++p]=sa[i]-j;
        for(i=1;i<=n;i++)
            ++c[x[y[i]]];
        for(i=1;i<=m;i++)
            c[i]+=c[i-1];
        for(i=n;i;i--)
            sa[c[x[y[i]]]--]=y[i];
        for(i=1;i<=m;i++)
            c[i]=0;
        swap(x,y);
        p=1;
        x[sa[1]]=1;
        for(i=2;i<=n;i++)
            x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j])?p:++p;
        m=p;
    }
    for(i=1;i<=n;i++)
        rk[sa[i]]=i;
    p=0;
    for(i=1;i<=n;i++){
        j=sa[rk[i]-1];
        if(p)--p;
        while(s[i+p]==s[j+p])++p;
        ht[rk[i]]=p;
    }
}

int main()
{
    scanf("%s",s+1);
    int l=strlen(s+1);
    gtsa(l,127);return 0;
}

如果实在不能理解,可以先看一下这份冗长而低效的方法:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=3e5+5;

char s[N];
int sa[N],rk[N],ht[N];
struct elm{
    int x,r1,r2;
    bool operator==(elm a)const{
        return r1==a.r1&&r2==a.r2;
    }
}p[N],q[N];
int sm[N];

void gtsa(int n,int m){
    int i,j;
    for(i=1;i<=n;i++)
        p[i]=(elm){i,s[i],0};
    for(i=1;i<=m;i++)
        sm[i]=0;
    for(i=1;i<=n;i++)
        ++sm[p[i].r2];
    for(i=1;i<=m;i++)
        sm[i]+=sm[i-1];
    for(i=n;i;i--)
        q[sm[p[i].r2]--]=p[i];
    for(i=1;i<=n;i++)
        p[i]=q[i];
    for(i=1;i<=m;i++)
        sm[i]=0;
    for(i=1;i<=n;i++)
        ++sm[p[i].r1];
    for(i=1;i<=m;i++)
        sm[i]+=sm[i-1];
    for(i=n;i;i--)
        q[sm[p[i].r1]--]=p[i];
    for(i=1;i<=n;i++)
        p[i]=q[i];
    m=1;
    q[1].r1=m;
    for(i=2;i<=n;i++)
        q[i].r1=(p[i]==p[i-1]?m:++m);
    for(i=1;i<=n;i++)
        p[i]=q[i];
    for(j=1;j<=n&&m<n;j<<=1){
        for(i=1;i<=n;i++)
            sm[i]=0;
        for(i=1;i<=n;i++)
            ++sm[p[i].x];
        for(i=1;i<=n;i++)
            sm[i]+=sm[i-1];
        for(i=n;i;i--)
            q[sm[p[i].x]--]=p[i];
        for(i=1;i<=n;i++)
            p[i]=q[i];
        for(i=n-j+1;i<=n;i++)
            p[i].r2=0;
        for(i=1;i<=n-j;i++)
            p[i].r2=p[i+j].r1;
        for(i=1;i<=m;i++)
            sm[i]=0;
        for(i=1;i<=n;i++)
            ++sm[p[i].r2];
        for(i=1;i<=m;i++)
            sm[i]+=sm[i-1];
        for(i=n;i;i--)
            q[sm[p[i].r2]--]=p[i];
        for(i=1;i<=n;i++)
            p[i]=q[i];
        for(i=1;i<=m;i++)
            sm[i]=0;
        for(i=1;i<=n;i++)
            ++sm[p[i].r1];
        for(i=1;i<=m;i++)
            sm[i]+=sm[i-1];
        for(i=n;i;i--)
            q[sm[p[i].r1]--]=p[i];
        for(i=1;i<=n;i++)
            p[i]=q[i];
        m=1;
        q[1].r1=m;
        for(i=2;i<=n;i++)
            q[i].r1=(p[i]==p[i-1]?m:++m);
        for(i=1;i<=n;i++)
            p[i]=q[i];
    }
    for(i=1;i<=n;i++)
        sa[i]=p[i].x,
        rk[p[i].x]=i;
    for(i=1,j=0;i<=n;i++){
        if(j)--j;
        while(s[i+j]==s[sa[rk[i]-1]+j])
            ++j;
        ht[rk[i]]=j;
    }
}

int main()
{
    scanf("%s",s+1);
    int l=strlen(s+1);
    gtsa(l,127);
    for(int i=1;i<=l;i++)
        printf("%d ",sa[i]);
    return 0;
}

更高效的算法请右转后缀自动机……

原文地址:https://www.cnblogs.com/l-ly-03/p/String-SuffixArray.html

时间: 2024-10-11 16:56:23

字符串-后缀数组的相关文章

Bzoj4556: [Tjoi2016&amp;Heoi2016]字符串 后缀数组

4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 169  Solved: 87[Submit][Status][Discuss] Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了 一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE O,嫁给高富帅,走上人生巅峰.

[XSY 1516] 兔子的字符串 后缀数组

题意 给定一个字符串 $S$ . 按照某种方式, 将字符串 $S$ 化成不超过 $K$ 段 $S_1, S_2, ..., S_K$ . 每段 $S_i$ 有字典序最大的子串 $C_i$ . 最小化 $C_i$ 的最大值. $N \le 200000$ . 分析 通过后缀数组, 先二分后缀, 再二分长度, 实现二分所有的字符串. 判定则可以贪心取, 利用后缀数组的信息, 记录 v[i] 表示位置 i 不能与位置 v[i] 在同一段中. 实现 #include <cstdio> #include

bzoj4556: [Tjoi2016&amp;Heoi2016]字符串 (后缀数组加主席树)

题目是给出一个字符串,每次询问一个区间[a,b]中所有的子串和另一个区间[c,d]的lcp最大值,首先求出后缀数组,对于lcp的最大值肯定是rank[c]的前驱和后继,但是对于这个题会出现问题,就是题目中有区间的限制. For example: 5 1 aaaab 1 2 3 5 对于这个样例,如果直接找到aab的前驱是 aaab,然后由于区间的原因答案是1,但是如果我们再往前找的话,找到aaaab,答案会变成2.那就出现了错误.考虑一下怎么做可以去除这种影响呢? 我们可以二分一下,首先对于[a

【hiho】120 后缀数组一&#183;重复旋律2【字符串--后缀数组--最长不可重叠重复子串问题】

传送门:后缀数组一·重复旋律2 题意 最长可重叠重复子串问题 思路 二分答案,转化成判定问题. 看看能不能找出不重叠的重复子串.对于每一组,我们检查这些后缀对应的sa值(也就是后缀起点在原串中的位置i).如果max{sa} - min{sa} >= k,那么就说明我们能找出一组不重叠的重复子串 AC Code #include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; c

字符串(后缀数组):POJ 3415 Common Substrings

Common Substrings Description A substring of a string T is defined as: T(i, k)=TiTi+1...Ti+k-1, 1≤i≤i+k-1≤|T|. Given two strings A, B and one integer K, we define S, a set of triples (i, j, k): S = {(i, j, k) | k≥K, A(i, k)=B(j, k)}. You are to give

【hiho】121 后缀数组一&#183;重复旋律2【字符串--后缀数组--最长可重叠重复K次子串问题】

传送门:https://hihocoder.com/contest/hiho121/problem/1 题意 最长可重叠重复K次子串问题 思路 二分答案,转化成判定问题. 看看能不能找出不重叠的重复子串.对于每一组,我们检查这些后缀对应的sa值(也就是后缀起点在原串中的位置i).如果max{sa} - min{sa} >= k,那么就说明我们能找出一组不重叠的重复子串 AC Code #include<bits/stdc++.h> #define rep(i,a,b) for(int i

字符串(后缀数组):POJ 3294 Life Forms

Life Forms Description You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial traits such as height, colour, wrinkles, ears, eyebrows and the like. A few bear no human resemblance; these typically have ge

bzoj 2251: [2010Beijing Wc]外星联络 后缀数组

2251: [2010Beijing Wc]外星联络 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 424  Solved: 232[Submit][Status][Discuss] Description 小 P 在看过电影<超时空接触>(Contact)之后被深深的打动,决心致力于寻 找外星人的事业.于是,他每天晚上都爬在屋顶上试图用自己的收音机收听外星 人发来的信息.虽然他收听到的仅仅是一些噪声,但是他还是按照这些噪声的高 低电平将接收到

「kuangbin带你飞」专题十八 后缀数组

layout: post title: 「kuangbin带你飞」专题十八 后缀数组 author: "luowentaoaa" catalog: true tags: - kuangbin - 字符串 - 后缀数组 传送门 倍增法 struct DA{ bool cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; } int t1[maxn],t2[maxn],c[maxn]; int r