【后缀数组学习】

原文转载自博客,但有部分修改。

模板

后缀数组是解决一系列字符串题目的利器,后缀数组中保留了这样的信息。sa[i]表示排名为第 i 位的后缀是从sa[i]开始的。通过倍增算法可以在O(nlogn)的时间复杂度内将所有的后缀进行排序。而height数组也是在处理问题中经常要使用到的,height[i]表示排名第 i 的后缀与排名第 i-1 位的后缀的最长公共前缀的长度。具体见代码。

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 100005;
int wa[N],wb[N],wv[N],ws[N];
int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m)
{
    int i,j,p,*x=wa,*y=wb;
    // 下面四行是对第一个字母的一个基数排序:基数排序其实就是记录前面有多少个位置被占据了 
    for(i=0;i<m;i++) ws[i]=0; // 将统计字符数量的数组清空 
    for(i=0;i<n;i++) ws[x[i]=r[i]]++; // 统计各种字符的个数 
    for(i=1;i<m;i++) ws[i]+=ws[i-1]; // 进行一个累加,因为前面的小字符集对后面字符的排位有位置贡献 
    for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i; // 根据位置来排序,sa[x] = i,表示i位置排在第x位
    // wa[x[i]]就是字符集0-x[i]共有多少字符占据了位置,减去自己的一个位置剩下的就是自己的排名了,排名从0开始 
    // 排名过程中主要的过程是对于处于相同字符的字符的排序,因为改变wa[x[i]]值得只会是本身,小于该字符的贡献值
    // 是不变的,对于第一个字符相同的依据是位置关系,在后面将看到通过第二个关键字来确定相同字符的先后关系 


    // 这以后的排序都是通过两个关键字来确定一个串的位置,也即倍增思想
    // 通过将一个串分解成两部分,而这两部分的位置关系我们都已经计算出来 
    for(j=1,p=1;p<n;j*=2,m=p) 
    {
        for(p=0,i=n-j;i<n;i++) y[p++]=i; // 枚举的串是用于与i位置的串进行合并,由于i较大,因为匹配的串为空串 
        // 由于枚举的是长度为j的串,那么i位置开始的串将凑不出这个长度的串,因此第二关键字应该最小,这其中位置靠前的较小
        for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; // sa[i]-j开头的串作为第二关键字与编号为sa[i]的串匹配,sa[i]<j的串不用作为第二关键字来匹配 
        for(i=0;i<n;i++) wv[i]=x[y[i]]; // 取出这些位置的第一关键字 
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[wv[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i]; // 按照第二关键字进行第一关键字的基数排序 
        for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++) // 对排好序的sa数组进行一次字符集缩小、常数优化 
        x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
    return;
}

int rank[N],height[N];
void calheight(int *r,int *sa,int n) // 这里的n是原串的本来长度,即不包括新增的0 
{
    int i,j,k=0;
    for(i=1;i<=n;i++) rank[sa[i]]=i; // 有后缀数组得到名次数组,排名第0的后缀一定是添加的0 
    for(i=0;i<n;height[rank[i++]]=k) // 以 i 开始的后缀总能够从以 i-1 开始的后缀中继承 k-1 匹配项出来 
    for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); // 进行一个暴力的匹配,但是整个算法的时间复杂度还是O(n)的 
    return;
}

int main() {

    return 0;    
}

附上一个去注释版。

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 10005;
int wa[N], wb[N], ws[N], wv[N];
int rank[N], height[N];

bool cmp(int r[], int a, int b, int l) {
    return r[a] == r[b] && r[a+l] == r[b+l];
}

void da(int r[], int sa[], int n, int m) {
    int i, j, p, *x = wa, *y = wb;
    for (i = 0; i < m; ++i) ws[i] = 0;
    for (i = 0; i < n; ++i) ws[x[i]=r[i]]++;
    for (i = 1; i < m; ++i) ws[i] += ws[i-1];
    for (i = n-1; i >= 0; --i) sa[--ws[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;
        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) ws[i] = 0;
        for (i = 0; i < n; ++i) ws[wv[i]]++;
        for (i = 1; i < m; ++i) ws[i] += ws[i-1];
        for (i = n-1; i >= 0; --i) sa[--ws[wv[i]]] = y[i];
        for (swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
    }
}

void calheight(int r[], int sa[], int n) {
    int i, j, k = 0;
    for (i = 1; i <= n; ++i) rank[sa[i]] = i;
    for (i = 0; i < n; height[rank[i++]] = k)
        for (k?k--:0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k++);
}

int main() {

    return 0;    
}

底下的模板用的是封装过的模板。


重复子串:

字符串R在字符串L中至少出现两次,则称R是L的重复子串

1)可重叠性最长重复子串:求出height数组最大的一个值即可。

2)不可重叠最长重复子串:二分枚举长度,将原问题转化为一个判定性的问题,若枚举的长度为k那么根据height值进行分组,连续的height值大于等于k的分为一组,通过查看一组内部的最大后缀编号和最小后缀编号差值是否大于等于k来判定。

POJ-1743 Musical Theme

题意:给定若干个数字,其大小在1-88之间,现在要出要出这个串中最长的不重复的两个相同的子串,满足要求的子串的定义是长度不短于5,其两个子串可以通过分别加上某一值或减去某一值得到,串不能够有公共部分。

分析:首先通过该串的后一项减去前一项得到一个反应差值的新序列,然后再对这个新序列求sa数组即height数组,使用二分答案的方式来解决这个问题,需要注意的是转化为差值之后,在计算编号差的时候不能够取等号,否则将会使得共用一个元素的情况发生。

//  POJ 1743.cpp
//
//  Created by whn6325689 on 15/9/15.
//  Copyright (c) 2015年 whn6325689. All rights reserved.
//
//  Mr.Phoebe
//  http://blog.csdn.net/u013007900
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>
#include <functional>
#include <numeric>
#pragma comment(linker, "/STACK:1024000000,1024000000")


using namespace std;

#define eps 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LLINF 1LL<<62
#define speed std::ios::sync_with_stdio(false);

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef complex<ld> point;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef vector<int> vi;

#define CLR(x,y) memset(x,y,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define clr(a,x,size) memset(a,x,sizeof(a[0])*(size))
#define cpy(a,x,size) memcpy(a,x,sizeof(a[0])*(size))
#define debug(a) cout << #a" = " << (a) << endl;
#define debugarry(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }

#define mp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define lowbit(x) (x&(-x))

#define MID(x,y) (x+((y-x)>>1))
#define ls (idx<<1)
#define rs (idx<<1|1)
#define lson ls,l,mid
#define rson rs,mid+1,r

template<class T>
inline bool read(T &n)
{
    T x = 0, tmp = 1;
    char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == EOF) return false;
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return true;
}
template <class T>
inline void write(T n)
{
    if(n < 0)
    {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n)
    {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}
//-----------------------------------

const int MAXN=20005;

class Suffix_Array
{
public:
    int sa[MAXN],t[MAXN],t2[MAXN],cnt[MAXN],rank[MAXN],height[MAXN];

    void build_sa(int *s,int n,int m)
    {
        int *x=t,*y=t2;
        for(int i=0; i<m; i++) cnt[i]=0;
        for(int i=0; i<n; i++) cnt[x[i]=s[i]]++;
        for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
        for(int i=0; i<n; i++) sa[--cnt[x[i]]] = i;
        for(int k=1; k<=n; k<<=1)
        {
            int 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++) cnt[i]=0;
            for(int i=0; i<n; i++) cnt[x[y[i]]]++;
            for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
            for(int i=n-1; i>=0; i--) sa[--cnt[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-1]+k]==y[sa[i]+k]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }

    void getHeight(int *s,int n)
    {
        int k=0;
        for(int i=0; i<=n; i++) rank[sa[i]]=i;
        for(int i=0; i<n; i++)
        {
            if(k) k--;
            int j=sa[rank[i]-1];
            while(j+k<n && i+k<n && s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }

} sa;

int n;
int seq[MAXN];

bool check(int mid)
{
    int minn,maxx;
    for(int i=2; i<=n; i++)
    {
        minn=sa.sa[i-1];
        maxx=sa.sa[i-1];
        while(sa.height[i]>=mid)
        {
            minn=min(minn,sa.sa[i]);
            maxx=max(maxx,sa.sa[i]);
            i++;
        }
        if(maxx-minn>mid)   return true;// 因为我们是取了两个数之间的差,导致下标移位,所以不能够取到等于,
    }
    return false;
}

int solve()
{
    int l=4,r=n/2;
    int ans=-1;
    while(l<=r)
    {
        int mid=MID(l,r);
        if(check(mid))
        {
            l=mid+1;
            ans=mid;
        }
        else
            r=mid-1;
    }
    return ans+1;
}

int main()
{
    while(read(n)&&n)
    {
        for(int i=0; i<n; i++)
            read(seq[i]);
        if(n<=9)
        {
            puts("0");
            continue;
        }
        for (int i=1; i<n; i++)
            seq[i-1]=88+seq[i]-seq[i-1];
        seq[n]=0;
        sa.build_sa(seq,n,180);
        sa.getHeight(seq,n);
        printf("%d\n",solve());
    }
    return 0;
}

3)可重叠的K次最长重复子串:要求串中该子串至少出现K次,但是某一段字符串可以贡献于多个子串。做法是二分枚举长度,然后与第2种一样进行分组,不同的这里需要统计同一组的后缀个数是否大于等于K即可。

POJ-3261 Milk Patterns

题意:给定一系列的数字串,求出这其中出现次数不低于K次的最长的子串,题目保证有结果。

分析:由于题目中给定的数字集合较大因先对数字进行离散化处理。然后通过后缀数组求出sa和height数组后,二分枚举长度进行分组,统计一组内的后缀个数是否大于K。

//
//  POJ 3261.cpp
//
//  Created by whn6325689 on 15/9/15.
//  Copyright (c) 2015年 whn6325689. All rights reserved.
//
//      Mr.Phoebe
//      http://blog.csdn.net/u013007900
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>
#include <functional>
#include <numeric>
#pragma comment(linker, "/STACK:1024000000,1024000000")


using namespace std;

#define eps 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LLINF 1LL<<62
#define speed std::ios::sync_with_stdio(false);

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef complex<ld> point;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef vector<int> vi;

#define CLR(x,y) memset(x,y,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define clr(a,x,size) memset(a,x,sizeof(a[0])*(size))
#define cpy(a,x,size) memcpy(a,x,sizeof(a[0])*(size))
#define debug(a) cout << #a" = " << (a) << endl;
#define debugarry(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }

#define mp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define lowbit(x) (x&(-x))

#define MID(x,y) (x+((y-x)>>1))
#define ls (idx<<1)
#define rs (idx<<1|1)
#define lson ls,l,mid
#define rson rs,mid+1,r

template<class T>
inline bool read(T &n)
{
    T x = 0, tmp = 1;
    char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == EOF) return false;
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return true;
}
template <class T>
inline void write(T n)
{
    if(n < 0)
    {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n)
    {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}
//-----------------------------------

const int MAXN=20005;

class Suffix_Array
{
public:
    int sa[MAXN],t[MAXN],t2[MAXN],cnt[MAXN],rank[MAXN],height[MAXN];

    void build_sa(int *s,int n,int m)
    {
        int *x=t,*y=t2;
        for(int i=0; i<m; i++) cnt[i]=0;
        for(int i=0; i<n; i++) cnt[x[i]=s[i]]++;
        for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
        for(int i=0; i<n; i++) sa[--cnt[x[i]]] = i;
        for(int k=1; k<=n; k<<=1)
        {
            int 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++) cnt[i]=0;
            for(int i=0; i<n; i++) cnt[x[y[i]]]++;
            for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
            for(int i=n-1; i>=0; i--) sa[--cnt[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-1]+k]==y[sa[i]+k]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }

    void getHeight(int *s,int n)
    {
        int k=0;
        for(int i=0; i<=n; i++) rank[sa[i]]=i;
        for(int i=0; i<n; i++)
        {
            if(k) k--;
            int j=sa[rank[i]-1];
            while(j+k<n && i+k<n && s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }

} sa;

int n,k;
int seq[MAXN],val[MAXN];

bool check(int mid)
{
    int tot;
    for(int i=2; i<=n; i++)
    {
        tot=1;
        while(sa.height[i]>=mid)
        {
            i++;tot++;
        }
        if(tot>=k)   return true;
    }
    return false;
}

int solve()
{
    int l=0,r=n;
    int ans=0;
    while(l<=r)
    {
        int mid=MID(l,r);
        if(check(mid))
        {
            l=mid+1;
            ans=mid;
        }
        else
            r=mid-1;
    }
    return ans;
}

int main()
{
    //freopen("data.txt","r",stdin);
    while(read(n)&&read(k))
    {
        for(int i=0; i<n; i++)
        {
            read(seq[i]);
            val[i]=seq[i];
        }
        sort(val,val+n);
        int cnt=unique(val,val+n)-val;
        for (int i=0; i<n; i++)
            seq[i]=lower_bound(val,val+cnt,seq[i])-val+1;
        seq[n]=0;
        sa.build_sa(seq,n+1,cnt+1);
        sa.getHeight(seq,n+1);
        printf("%d\n",solve());
    }
    return 0;
}

连续重复子串:

如果一个字符串L是有某个字符串S重复R次而得到,则称L是一个连续重复串。R是这个字符串的重复次数

1)连续重复子串:给定一个串L,一直这个字符串是由某个字符串S重复R次而得到的,求R的最大值。求出sa和height数组后,枚举重复的串的长度K(K为L的约数),判定只需求suffix(1)和suffix(1+k)的最长公共前缀是否等于n-k,其实也就等价于KMP算法的含义了,计算任意一个后缀与第一个后缀的最长公共前缀。

POJ-2406 Power Strings

题意:给定一个字符串,已知其实一个字符串重复n次而来,求出最大的n。

分析:按照上面的解法,倍增算法TLE,DC3勉强能过。下面贴个KMP的代码。

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 1000005;
char str[N];
int next[N];

void getnext(char str[], int n) {
    int i, j;
    next[0] = -1;
    for (i = 1, j = 0; i < n; ) {
        if (j == -1 || str[i] == str[j]) {
            ++i, ++j;
            next[i] = j;
        } else {
            j = next[j];
        }
    }
}

int main() {
    while (scanf("%s", str), str[0] != '.') {
        int len = strlen(str);
        getnext(str, len);
        if (len % (len-next[len]) != 0) puts("1");
        else printf("%d\n", len / (len-next[len]));
    }
    return 0;    
}

2)重复次数最多的连续重复子串:给定一个字符串,求出重复次数最多的连续重复子串。
解决这个问题的过程不像前面那些问题那么直接了。
按步骤来,首先求出sa数组及height数组,接着枚举连续重复子串中不断重复那部分字符串的长度,称这个串为元串,长度为 i
1 max(len/2,1) ,枚举的元串假设其重复次数至少为两次,那么其一定会跨过两个 i 的整数倍点,因为如果只跨过一个点的话,其最长长度为 2i1 ,不满足重复至少两次的要求;
首先假设连续重复子串出现的位置是 ij ,对两个整数倍点 ij,ij+i 求一次LCP,得出的长度为该元串在 ij 位置能够重复出现的最长长度 L ,将这个长度除以i 1 将得到重复次数L/i+1
其次假设起点在 [iji+1,ijd] 之间,其中 d=iL ,(上面介绍了这个串会跨过两个整数点)其意义为根据已知的匹配长度,可以将起点往前移动的范围。
如果要求出某个最多的连续重复子串的最小字典序子需要枚举所有起点,但如果只是要的到最多的重复次数或者任意最多的连续重复子串,那么只需要枚举 ijd 处的起点即可,因为后面的起点若能够得到最优的结果,那么 ijd 处也一定能得到,且答案一样均为 L/i+2

SPOJ-687 Repeats

题意:给定一个字符串,求出重复次数最多的连续重复子串的重复次数。
分析:直接求即可,每次只需要枚举除整数倍之外的一个起点。

//      whn6325689
//      Mr.Phoebe
//      http://blog.csdn.net/u013007900
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>
#include <functional>
#include <numeric>
#pragma comment(linker, "/STACK:1024000000,1024000000")


using namespace std;

#define eps 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LLINF 1LL<<62
#define speed std::ios::sync_with_stdio(false);

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef complex<ld> point;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef vector<int> vi;

#define CLR(x,y) memset(x,y,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define clr(a,x,size) memset(a,x,sizeof(a[0])*(size))
#define cpy(a,x,size) memcpy(a,x,sizeof(a[0])*(size))
#define debug(a) cout << #a" = " << (a) << endl;
#define debugarry(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }

#define mp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define lowbit(x) (x&(-x))

#define MID(x,y) (x+((y-x)>>1))
#define getidx(l,r) (l+r|l!=r)
#define ls getidx(l,mid)
#define rs getidx(mid+1,r)
#define lson l,mid
#define rson mid+1,r

template<class T>
inline bool read(T &n)
{
    T x = 0, tmp = 1;
    char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == EOF) return false;
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return true;
}
template <class T>
inline void write(T n)
{
    if(n < 0)
    {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n)
    {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}
//-----------------------------------

const int MAXN=50010;

class Suffix_Array
{
public:
    int sa[MAXN],t[MAXN],t2[MAXN],cnt[MAXN],rank[MAXN],height[MAXN];
    int f[MAXN][30];

    void build_sa(int *s,int n,int m)
    {
        int *x=t,*y=t2;
        for(int i=0; i<m; i++) cnt[i]=0;
        for(int i=0; i<n; i++) cnt[x[i]=s[i]]++;
        for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
        for(int i=0; i<n; i++) sa[--cnt[x[i]]] = i;
        for(int k=1; k<=n; k<<=1)
        {
            int 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++) cnt[i]=0;
            for(int i=0; i<n; i++) cnt[x[y[i]]]++;
            for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
            for(int i=n-1; i>=0; i--) sa[--cnt[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-1]+k]==y[sa[i]+k]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }

    void getHeight(int *s,int n)
    {
        int k=0;
        for(int i=0; i<=n; i++) rank[sa[i]]=i;
        for(int i=0; i<n; i++)
        {
            if(k) k--;
            int j=sa[rank[i]-1];
            while(j+k<n && i+k<n && s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }

    void rmqinit(int n)
    {
        int LIM=(int)log2(1.0*n);
        for (int i=1; i<=n; i++)
            f[i][0]=height[i];
        for (int j=1; j<=LIM; j++)
            for (int i=1; i+(1<<j)-1<=n; i++)
                f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
    }

    int query(int l,int r)
    {
        int k=(int)log2(1.0*(r-l+1));
        return min(f[l][k],f[r-(1<<k)+1][k]);
    }

} sa;

int seq[MAXN];
char str[15];

int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=0; i<n; i++)
        {
            scanf("%s",str);
            seq[i]=str[0]-'a'+1;
        }
        seq[n]=0;
        sa.build_sa(seq,n+1,3);
        sa.getHeight(seq,n);
        sa.rmqinit(n);
        int ans=1;
        for(int i=1; i<=n/2; i++)
        {
            for(int j=0; (j+1)*i<n; j++)
            {
                int l=sa.rank[i*j],r=sa.rank[i*(j+1)];
                if(l>r) swap(l,r);
                int L=sa.query(l+1,r),d=i-L%i;
                ans=max(ans,L/i+1);
                if(d && j)
                {
                    l=sa.rank[i*j-d],r=sa.rank[i*(j+1)-d];
                    if(l>r) swap(l,r);
                    L=sa.query(l+1,r);
                }
                ans=max(ans,L/i+1);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

POJ-3693 Maximum repetition substring

题意:给定一个字符串,求出重复次数最多的连续重复子串,优先重复次数最多,否则输出字典序最小的子串。

分析:与上题不同的是需要枚举多个起点,因此尽管答案相同,可能字典序较之要小。用到了string的一个赋值函数str.assign(const char *, int, int);,用于取出某个字符串的一段子串。

//      whn6325689
//      Mr.Phoebe
//      http://blog.csdn.net/u013007900
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>
#include <functional>
#include <numeric>
#pragma comment(linker, "/STACK:1024000000,1024000000")


using namespace std;

#define eps 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LLINF 1LL<<62
#define speed std::ios::sync_with_stdio(false);

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef complex<ld> point;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef vector<int> vi;

#define CLR(x,y) memset(x,y,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define clr(a,x,size) memset(a,x,sizeof(a[0])*(size))
#define cpy(a,x,size) memcpy(a,x,sizeof(a[0])*(size))
#define debug(a) cout << #a" = " << (a) << endl;
#define debugarry(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }

#define mp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define lowbit(x) (x&(-x))

#define MID(x,y) (x+((y-x)>>1))
#define getidx(l,r) (l+r|l!=r)
#define ls getidx(l,mid)
#define rs getidx(mid+1,r)
#define lson l,mid
#define rson mid+1,r

template<class T>
inline bool read(T &n)
{
    T x = 0, tmp = 1;
    char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == EOF) return false;
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return true;
}
template <class T>
inline void write(T n)
{
    if(n < 0)
    {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n)
    {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}
//-----------------------------------

const int MAXN=100010;

class Suffix_Array
{
public:
    int sa[MAXN],t[MAXN],t2[MAXN],cnt[MAXN],rank[MAXN],height[MAXN];
    int f[MAXN][30];

    void build_sa(int *s,int n,int m)
    {
        int *x=t,*y=t2;
        for(int i=0; i<m; i++) cnt[i]=0;
        for(int i=0; i<n; i++) cnt[x[i]=s[i]]++;
        for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
        for(int i=0; i<n; i++) sa[--cnt[x[i]]] = i;
        for(int k=1; k<=n; k<<=1)
        {
            int 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++) cnt[i]=0;
            for(int i=0; i<n; i++) cnt[x[y[i]]]++;
            for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
            for(int i=n-1; i>=0; i--) sa[--cnt[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-1]+k]==y[sa[i]+k]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }

    void getHeight(int *s,int n)
    {
        int k=0;
        for(int i=0; i<=n; i++) rank[sa[i]]=i;
        for(int i=0; i<n; i++)
        {
            if(k) k--;
            int j=sa[rank[i]-1];
            while(j+k<n && i+k<n && s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }

    void rmqinit(int n)
    {
        int LIM=(int)log2(1.0*n);
        for (int i=1; i<=n; i++)
            f[i][0]=height[i];
        for (int j=1; j<=LIM; j++)
            for (int i=1; i+(1<<j)-1<=n; i++)
                f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
    }

    int query(int l,int r)
    {
        int k=(int)log2(1.0*(r-l+1));
        return min(f[l][k],f[r-(1<<k)+1][k]);
    }

} sa;

int seq[MAXN];
char str[MAXN];

string solve(int len)
{
    int maxx=0,lim=max(len/2,1);
    string tmp,ans;
    for (int i=1; i<=lim; i++)
    {
        for(int j=0; i*j+i<len; j++)
        {
            int l=sa.rank[i*j],r=sa.rank[i*j+i];
            if(l>r) swap(l,r);
            int x=sa.query(l+1,r);
            if(x/i+1>=maxx)
            {
                tmp.assign(str,i*j,i*(x/i+1));
                if(x/i+1==maxx)
                {
                    if(ans>tmp || !ans.length()) ans=tmp;
                }
                else
                {
                    maxx=x/i+1;
                    ans=tmp;
                }
            }
            int d=i-x%i;
            if(d && j)
            {
                for(int k=d; k<i; k++)
                {
                    l=sa.rank[i*j-k],r=sa.rank[i*j+i-k];
                    if(l>r) swap(l,r);
                    int y=sa.query(l+1,r);
                    if(y/i+1>=maxx)
                    {
                        tmp.assign(str,i*j-k,i*(y/i+1));
                        if(maxx==y/i+1)
                        {
                            if(ans>tmp || !ans.length()) ans=tmp;
                        }
                        else
                        {
                            maxx=y/i+1;
                            ans=tmp;
                        }
                    }
                    else break;
                }
            }
        }
    }
    return ans;
}

int main()
{
    int cas=1;
    while(scanf("%s",str)!=EOF && str[0]!='#')
    {
        int len=strlen(str);
        for (int i=0; i<len; i++)
            seq[i]=str[i]-'a'+1;
        seq[len]=0;
        sa.build_sa(seq,len+1,27);
        sa.getHeight(seq,len);
        sa.rmqinit(len);
        printf("Case %d: %s\n",cas++,solve(len).c_str());
    }
    return 0;
}

公共子串:

如果字符串L同时出现在字符串A和字符串B中,则称字符串L是字符串A和字符串B的公共子串

1)最长公共子串:给定两个字符串A和B,求最长公共子串。首先将两个字符串合并成一个字符串,通过二分枚举长度,然后在height分组中查看是否存在在两个不同串的后缀即可。也可以证明最长的公共子串所在后缀是相邻的,因此可以直接遍历一遍height数组,判定相邻的两个height数组是否属于不同的两个串。

POJ-2774 Long Long Message

题意:给定两个串,求出最长的公共子串的长度。
方法:按照上述方法可以,直接这样贪心地做也行。

if(0<=sa[i-1]&&sa[i-1]<len&&len<sa[i])
maxx=height[i];
if(0<=sa[i]&&sa[i]<len&&len<sa[i-1])
maxx=height[i];

二分

//      whn6325689
//      Mr.Phoebe
//      http://blog.csdn.net/u013007900
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>
#include <functional>
#include <numeric>
#pragma comment(linker, "/STACK:1024000000,1024000000")


using namespace std;

#define eps 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LLINF 1LL<<62
#define speed std::ios::sync_with_stdio(false);

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef complex<ld> point;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef vector<int> vi;

#define CLR(x,y) memset(x,y,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define clr(a,x,size) memset(a,x,sizeof(a[0])*(size))
#define cpy(a,x,size) memcpy(a,x,sizeof(a[0])*(size))
#define debug(a) cout << #a" = " << (a) << endl;
#define debugarry(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }

#define mp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define lowbit(x) (x&(-x))

#define MID(x,y) (x+((y-x)>>1))
#define getidx(l,r) (l+r|l!=r)
#define ls getidx(l,mid)
#define rs getidx(mid+1,r)
#define lson l,mid
#define rson mid+1,r

template<class T>
inline bool read(T &n)
{
    T x = 0, tmp = 1;
    char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == EOF) return false;
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return true;
}
template <class T>
inline void write(T n)
{
    if(n < 0)
    {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n)
    {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}
//-----------------------------------

const int MAXN=200010;

class Suffix_Array
{
public:
    int sa[MAXN],t[MAXN],t2[MAXN],cnt[MAXN],rank[MAXN],height[MAXN];
    int f[MAXN][30];

    void build_sa(int *s,int n,int m)
    {
        int *x=t,*y=t2;
        for(int i=0; i<m; i++) cnt[i]=0;
        for(int i=0; i<n; i++) cnt[x[i]=s[i]]++;
        for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
        for(int i=0; i<n; i++) sa[--cnt[x[i]]] = i;
        for(int k=1; k<=n; k<<=1)
        {
            int 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++) cnt[i]=0;
            for(int i=0; i<n; i++) cnt[x[y[i]]]++;
            for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
            for(int i=n-1; i>=0; i--) sa[--cnt[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-1]+k]==y[sa[i]+k]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }

    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        for(i=1; i<=n; i++) rank[sa[i]]=i;
        for(i=0; i<n; height[rank[i++]]=k)
            for(k?k--:0,j=sa[rank[i]-1]; s[i+k]==s[j+k]; k++) ;
    }

} sa;

int seq[MAXN];
char s1[MAXN],s2[MAXN];

bool check(int mid,int n,int len1)
{
    for (int i=2; i<=n; i++)
    {
        int minn=sa.sa[i-1],maxx=sa.sa[i-1];
        while(i<=n && sa.height[i]>=mid)
        {
            minn=min(minn,sa.sa[i]);
            maxx=max(maxx,sa.sa[i]);
            i++;
        }
        if(minn<len1 && maxx>len1) return true;
    }
    return false;
}

int solve(int len,int len1,int len2)
{
    int ans=0,l=1,r=min(len1,len2);
    while(l<=r)
    {
        int mid=MID(l,r);
        if(check(mid,len,len1))
        {
            ans=mid;
            l=mid+1;
        }
        else
        {
            r=mid-1;
        }
    }
    return ans;
}

int main()
{
//    freopen("data.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    while(scanf("%s %s",s1,s2)!= EOF)
    {
        int len1=strlen(s1);
        int len2=strlen(s2);
        for(int i=0; i<len1; i++)
        {
            seq[i]=s1[i]-'a'+2;
        }
        seq[len1]=1;
        for(int i=0; i<len2; i++)
        {
            seq[len1+i+1]=s2[i]-'a'+2;
        }
        seq[len1+len2+1]=0;
        sa.build_sa(seq,len1+len2+2,30);
        sa.getHeight(seq,len1+len2+1);
        printf("%d\n",solve(len1+len2+1,len1,len2));
    }
    return 0;
}

URAL-1517 Freedom of Choice

题意:较之上题,该题要求输出任意一个最长的公共子串,求解的时候保留一下子串的位置信息即可。

//      whn6325689
//      Mr.Phoebe
//      http://blog.csdn.net/u013007900
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>
#include <functional>
#include <numeric>
#pragma comment(linker, "/STACK:1024000000,1024000000")


using namespace std;

#define eps 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LLINF 1LL<<62
#define speed std::ios::sync_with_stdio(false);

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef complex<ld> point;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef vector<int> vi;

#define CLR(x,y) memset(x,y,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define clr(a,x,size) memset(a,x,sizeof(a[0])*(size))
#define cpy(a,x,size) memcpy(a,x,sizeof(a[0])*(size))
#define debug(a) cout << #a" = " << (a) << endl;
#define debugarry(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }

#define mp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define lowbit(x) (x&(-x))

#define MID(x,y) (x+((y-x)>>1))
#define getidx(l,r) (l+r|l!=r)
#define ls getidx(l,mid)
#define rs getidx(mid+1,r)
#define lson l,mid
#define rson mid+1,r

template<class T>
inline bool read(T &n)
{
    T x = 0, tmp = 1;
    char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == EOF) return false;
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return true;
}
template <class T>
inline void write(T n)
{
    if(n < 0)
    {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n)
    {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}
//-----------------------------------

const int MAXN=200010;

class Suffix_Array
{
public:
    int sa[MAXN],t[MAXN],t2[MAXN],cnt[MAXN],rank[MAXN],height[MAXN];
    int f[MAXN][30];

    void build_sa(int *s,int n,int m)
    {
        int *x=t,*y=t2;
        for(int i=0; i<m; i++) cnt[i]=0;
        for(int i=0; i<n; i++) cnt[x[i]=s[i]]++;
        for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
        for(int i=0; i<n; i++) sa[--cnt[x[i]]] = i;
        for(int k=1; k<=n; k<<=1)
        {
            int 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++) cnt[i]=0;
            for(int i=0; i<n; i++) cnt[x[y[i]]]++;
            for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
            for(int i=n-1; i>=0; i--) sa[--cnt[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-1]+k]==y[sa[i]+k]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }

    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        for(i=1; i<=n; i++) rank[sa[i]]=i;
        for(i=0; i<n; height[rank[i++]]=k)
            for(k?k--:0,j=sa[rank[i]-1]; s[i+k]==s[j+k]; k++) ;
    }

} sa;

int seq[MAXN];
char s1[MAXN],s2[MAXN];

bool check(int mid,int n,int len1,string& tmp)
{
    for (int i=2; i<=n; i++)
    {
        int minn=sa.sa[i-1],maxx=sa.sa[i-1];
        while(i<=n && sa.height[i]>=mid)
        {
            minn=min(minn,sa.sa[i]);
            maxx=max(maxx,sa.sa[i]);
            i++;
        }
        if(minn<len1 && maxx>len1)
        {
            tmp.assign(s1,minn,mid);
            return true;
        }
    }
    return false;
}

string solve(int len,int n)
{
    int l=1,r=n;
    string ans;
    while(l<=r)
    {
        int mid=MID(l,r);
        if(check(mid,len,n,ans))
        {
            l=mid+1;
        }
        else
        {
            r=mid-1;
        }
    }
    return ans;
}

int main()
{
//    freopen("data.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    int n;
    while(scanf("%d",&n)!= EOF)
    {
        scanf("%s %s",s1,s2);
        for(int i=0; i<n; i++)
        {
            seq[i]=s1[i]-'A'+2;
        }
        seq[n]=1;
        for(int i=0; i<n; i++)
        {
            seq[n+i+1]=s2[i]-'A'+2;
        }
        seq[2*n+1]=0;
        sa.build_sa(seq,2*n+2,30);
        sa.getHeight(seq,2*n+1);
        printf("%s\n",solve(2*n+1,n).c_str());
    }
    return 0;
}

2)公共子串的个数:给定两个字符串A和B,求长度不小于K的公共子串的个数。将串B添加到A之后,中间使用一个特殊字符分隔开(为了防止两个后缀串的公共前缀跨越两个字符串),求出sa数组和height数组后,扫描一遍height数组,并且进行分组,分组的时候要维护一个height值单调上升的栈,栈中的每一个元素拥有两个属性,第一个是其值为多少,第二个是前面还有多少个能够提供这个值的(如果新加入的height值比之前较小时,将回收之前的height值,将其视为同一高度,直到遇到比它小的)。需要对height数组作两次。
(ok,我仔细瞅了瞅后缀数组的写法,觉得实在太麻烦,还是后缀自动自动机比较方便)

POJ-3415 Common Substrings

题意:求两个串的公共子串的个数。

分析:如上所述。

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <stack>
#include <algorithm>
using namespace std;

typedef long long LL;
const int N = 100005<<1;
const int inf = 0x3f3f3f3f;
int K, len1, len2, len;
char s1[N>>1], s2[N>>1];
int seq[N], sa[N], rank[N], height[N];
int wa[N], wb[N], ws[N], wv[N];

inline bool cmp(int r[], int a, int b, int l) {
    return r[a] == r[b] && r[a+l] == r[b+l];
}

void da(int r[], int sa[], int n, int m) {
    int i, j, p, *x = wa, *y = wb;
    for (i = 0; i < m; ++i) ws[i] = 0;
    for (i = 0; i < n; ++i) ws[x[i]=r[i]]++;
    for (i = 1; i < m; ++i) ws[i] += ws[i-1];
    for (i = n-1; i >= 0; --i) sa[--ws[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;
        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) ws[i] = 0;
        for (i = 0; i < n; ++i) ws[wv[i]]++;
        for (i = 1; i < m; ++i) ws[i] += ws[i-1];
        for (i = n-1; i >= 0; --i) sa[--ws[wv[i]]] = y[i];
        for (swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1 : p++;    
    }
}

void calheight(int r[], int sa[], int n) {
    int i, j, k = 0;
    for (i = 1; i <= n; ++i) rank[sa[i]] = i;
    for (i = 0; i < n; height[rank[i++]] = k)
        for (k?k--:0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; ++k) ;
}

int s[N][2];

void solve() {
    LL tot = 0, top = 0, sum = 0;
    for (int i = 1; i <= len; ++i) {
        if (height[i] < K) top = tot = 0; // 维护一个单调上升的栈
        else {
            int cnt = 0;
            if (sa[i-1] < len1) cnt++, tot += height[i]-K+1; // 如果前一个串是A串,那么将这个串加入到带匹配A串中 
            while (top > 0 && height[i] <= s[top-1][0]) {
                top--;
                tot -= s[top][1]*(s[top][0]-height[i]);
                cnt += s[top][1]; // s[top][1]伴随s[top][0]而用来计数的变量 
            }
            s[top][0] = height[i]; // 保留高度值 
            s[top++][1] = cnt; // 保留该处能够匹配到的串的个数 
            if (sa[i] > len1) sum += tot; // B串去匹配A串 
        }
    }
    tot = top = 0;
    for (int i = 1; i <= len; ++i) {
        if (height[i] < K) top = tot = 0;
        else {
            int cnt = 0;
            if (sa[i-1] > len1) cnt++, tot += height[i]-K+1;
            while (top > 0 && height[i] <= s[top-1][0]) {
                top--;
                tot -= s[top][1]*(s[top][0]-height[i]);
                cnt += s[top][1];    
            }    
            s[top][0] = height[i]; s[top++][1] = cnt;
            if (sa[i] < len1) sum += tot; // A串去匹配B串 
        }    
    }
    printf("%I64d\n", sum);
}

int main() {
    while (scanf("%d", &K), K) {
        scanf("%s %s", s1, s2);
        len1 = strlen(s1), len2 = strlen(s2);
        len = len1+len2+1;
        for (int i = 0; i < len1; ++i) seq[i] = s1[i];
        for (int i = 0; i < len2; ++i) {
            seq[len1+i+1] = s2[i];    
        }
        seq[len1] = 1, seq[len] = 0;
        da(seq, sa, len+1, 128);
        calheight(seq, sa, len);
        solve();
    }
    return 0;
}

多个字符串的相关问题

1)不小于K个字符串中的最长子串:同时出现在不少于K个串的子串。做法同样二分枚举长度,分组后在一组中寻找出,前缀出现过的原字符串数是否超过K即可。

POJ-3294 Life Forms

题意:给定N个串,求超过一半串拥有的最长子串。

分析:将所有的串连接起来后二分枚举长度分组。

//      whn6325689
//      Mr.Phoebe
//      http://blog.csdn.net/u013007900
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>
#include <functional>
#include <numeric>
#pragma comment(linker, "/STACK:1024000000,1024000000")


using namespace std;

#define eps 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LLINF 1LL<<62
#define speed std::ios::sync_with_stdio(false);

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef complex<ld> point;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef vector<int> vi;

#define CLR(x,y) memset(x,y,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define clr(a,x,size) memset(a,x,sizeof(a[0])*(size))
#define cpy(a,x,size) memcpy(a,x,sizeof(a[0])*(size))
#define debug(a) cout << #a" = " << (a) << endl;
#define debugarry(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }

#define mp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define lowbit(x) (x&(-x))

#define MID(x,y) (x+((y-x)>>1))
#define getidx(l,r) (l+r|l!=r)
#define ls getidx(l,mid)
#define rs getidx(mid+1,r)
#define lson l,mid
#define rson mid+1,r

template<class T>
inline bool read(T &n)
{
    T x = 0, tmp = 1;
    char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == EOF) return false;
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return true;
}
template <class T>
inline void write(T n)
{
    if(n < 0)
    {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n)
    {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}
//-----------------------------------

const int MAXN=100500;

int seq[MAXN],n;
char str[1111];
int vis[111],len[111];
vector<pii>v;

class Suffix_Array
{
public:
    int sa[MAXN],t[MAXN],t2[MAXN],cnt[MAXN],rank[MAXN],height[MAXN];
    int f[MAXN][30];

    void build_sa(int *s,int n,int m)
    {
        int *x=t,*y=t2;
        for(int i=0; i<m; i++) cnt[i]=0;
        for(int i=0; i<n; i++) cnt[x[i]=s[i]]++;
        for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
        for(int i=0; i<n; i++) sa[--cnt[x[i]]] = i;
        for(int k=1; k<=n; k<<=1)
        {
            int 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++) cnt[i]=0;
            for(int i=0; i<n; i++) cnt[x[y[i]]]++;
            for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
            for(int i=n-1; i>=0; i--) sa[--cnt[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-1]+k]==y[sa[i]+k]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }

    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        for(i=1; i<=n; i++) rank[sa[i]]=i;
        for(i=0; i<n; height[rank[i++]]=k)
            for(k?k--:0,j=sa[rank[i]-1]; s[i+k]==s[j+k]; k++) ;
    }

    int getid(int x)
    {
        int temp=sa[x];
        for(int i=0; i<n; i++)
        {
            if(temp>=(len[i]+1)) temp-=len[i]+1;
            else return i;
        }
        return n;
    }
} sa;

bool check(int mid,int len)
{
    int tot;
    vector<pii> vt;
    for(int i=2; i<=len; i++)
    {
        CLR(vis,0);
        tot=1;
        vis[sa.getid(i-1)]=1;
        while(sa.height[i] >= mid)
        {
            if(!vis[sa.getid(i)])
            {
                vis[sa.getid(i)]=1;
                tot++;
            }
            i++;
        }
        if(tot*2>n)
            vt.push_back(mp(sa.sa[i-1],mid));
    }
    if(vt.size())
    {
        v.clear();
        for(int i=0; i<vt.size(); i++)
            v.pb(vt[i]);
    }
    return vt.size();
}

void solve(int len,int minn)
{
    int l=1,r=minn;
    v.clear();
    while(l<=r)
    {
        int mid=MID(l,r);
        if(check(mid,len))  l=mid+1;
        else    r=mid-1;
    }
    if(!v.size())
    {
        puts("?");
        return;
    }
    for(int i=0; i<v.size(); i++)
    {
        for(int k=0,j=v[i].first; k<v[i].second; k++,j++)
            putchar(seq[j]);
        putchar('\n');
    }
}

int main()
{
//    freopen("data.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    int first=true;
    while(~scanf("%d",&n) && n)
    {
        if(first) first=false;
        else puts("");
        int tot=0,minn=1005;
        for(int i=0;i<n;i++)
        {
            scanf("%s",str);
            len[i]=strlen(str);
            minn=min(minn,len[i]);
            for(int j=0;j<len[i];j++)
            {
                seq[tot++]=str[j];
            }
            seq[tot++]=200+i;
        }
        seq[tot++]=0;
        sa.build_sa(seq,tot,305);
        sa.getHeight(seq,tot-1);
        solve(tot-1,minn);
    }
    return 0;
}

2)每个字符串至少出现两次且不可重叠的最长子串:二分枚举长度后在同一分组中对每一个字符串保留一个最小的位置和一个最大的位置,最后查看是否每个串在同一组中都有至少两个后缀,并且后缀的坐标差大于枚举的长度。

SPOJ-220 Relevant Phrases of Annihilation

题意:给定N个串,求每个串至少出现两次的最长子串。

分析:如上所述。

//      whn6325689
//      Mr.Phoebe
//      http://blog.csdn.net/u013007900
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>
#include <functional>
#include <numeric>
#pragma comment(linker, "/STACK:1024000000,1024000000")


using namespace std;

#define eps 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LLINF 1LL<<62
#define speed std::ios::sync_with_stdio(false);

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef complex<ld> point;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef vector<int> vi;

#define CLR(x,y) memset(x,y,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define clr(a,x,size) memset(a,x,sizeof(a[0])*(size))
#define cpy(a,x,size) memcpy(a,x,sizeof(a[0])*(size))
#define debug(a) cout << #a" = " << (a) << endl;
#define debugarry(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }

#define mp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define lowbit(x) (x&(-x))

#define MID(x,y) (x+((y-x)>>1))
#define getidx(l,r) (l+r|l!=r)
#define ls getidx(l,mid)
#define rs getidx(mid+1,r)
#define lson l,mid
#define rson mid+1,r

template<class T>
inline bool read(T &n)
{
    T x = 0, tmp = 1;
    char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == EOF) return false;
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return true;
}
template <class T>
inline void write(T n)
{
    if(n < 0)
    {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n)
    {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}
//-----------------------------------

const int MAXN=100050;

int seq[MAXN],n;
char str[10010];
int vis[15],len[15],pos[15][2];

class Suffix_Array
{
public:
    int sa[MAXN],t[MAXN],t2[MAXN],cnt[MAXN],rank[MAXN],height[MAXN];
    int f[MAXN][30];

    void build_sa(int *s,int n,int m)
    {
        int *x=t,*y=t2;
        for(int i=0; i<m; i++) cnt[i]=0;
        for(int i=0; i<n; i++) cnt[x[i]=s[i]]++;
        for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
        for(int i=0; i<n; i++) sa[--cnt[x[i]]] = i;
        for(int k=1; k<=n; k<<=1)
        {
            int 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++) cnt[i]=0;
            for(int i=0; i<n; i++) cnt[x[y[i]]]++;
            for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
            for(int i=n-1; i>=0; i--) sa[--cnt[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-1]+k]==y[sa[i]+k]?p-1:p++;
            if(p>=n) break;
            m=p;
        }
    }

    void getHeight(int *s,int n)
    {
        int i,j,k=0;
        for(i=1; i<=n; i++) rank[sa[i]]=i;
        for(i=0; i<n; height[rank[i++]]=k)
            for(k?k--:0,j=sa[rank[i]-1]; s[i+k]==s[j+k]; k++) ;
    }

    int getid(int x)
    {
        int temp=sa[x];
        for(int i=0; i<n; i++)
        {
            if(temp>=(len[i]+1)) temp-=len[i]+1;
            else return i;
        }
        return n;
    }
} sa;

bool check(int mid,int len)
{
    int idx;
    for(int i=2,k; i<=len; i++)
    {
        CLR(vis,0);
        idx=sa.getid(i-1);
        vis[idx]=1;
        pos[idx][0]=pos[idx][1]=sa.sa[i-1];
        while(sa.height[i] >= mid)
        {
            idx=sa.getid(i);
            if(!vis[idx])
            {
                vis[idx]=1;
                pos[idx][0]=pos[idx][1]=sa.sa[i];
            }
            else
            {
                vis[idx]++;
                pos[idx][0]=min(pos[idx][0],sa.sa[i]);
                pos[idx][1]=max(pos[idx][1],sa.sa[i]);
            }
            i++;
        }
        for(k=0; k<n; k++)
            if(vis[k]<2 || pos[k][1]-pos[k][0]<mid) break;
        if(k==n) return true;
    }
    return false;
}

int solve(int len,int minn)
{
    int l=1,r=minn/2;
    int ans=0;
    while(l<=r)
    {
        int mid=MID(l,r);
        if(check(mid,len))
        {
            ans=mid;
            l=mid+1;
        }
        else    r=mid-1;
    }
    return ans;
}

int main()
{
//    freopen("data.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int tot=0,minn=100005;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%s",str);
            len[i]=strlen(str);
            minn=min(minn,len[i]);
            for(int j=0;j<len[i];j++)
            {
                seq[tot++]=str[j];
            }
            seq[tot++]=200+i;
        }
        seq[tot++]=0;
        sa.build_sa(seq,tot,300);
        sa.getHeight(seq,tot-1);
        printf("%d\n",solve(tot-1,minn));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值