后缀数组整理

来源:互联网 发布:作图软件有cs? 编辑:程序博客网 时间:2024/06/08 07:13

学习整个后缀数组只需要看这一个论文即可。

地址:

http://files.cnblogs.com/files/newpanderking/%E5%90%8E%E7%BC%80%E6%95%B0%E7%BB%84%E2%80%94%E2%80%94%E5%A4%84%E7%90%86%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%9C%89%E5%8A%9B%E5%B7%A5%E5%85%B7.pdf


对于一个字符串的操作:




①最长公共前缀:
给定一个字符串,询问他的任意两个后缀的最长公共前缀。就是单纯的height数组




②可重叠的最长公共子串:
求两个重复子串,并且这两个子串是可以重叠的。求height数组的最大值即可。


③不可重叠的最长公共子串:
二分答案k,假设存在两个长度为k的不可重叠字串相同。
height[2]表示sa[1]和sa[2]的LCP,如果height[2]>=k(枚举的答案),
说明最长公共子串>=k了,但是我们要要求不重叠,也就是说sa[1]和
sa[2]起点差值要大于K才可以。我们判断sa[1],sa[2]的差值是否大于k即可


④出现K次的最长重复子串,可重叠。
遍历height数组,如果当前height<t,那么让num=1,否则就+1.
一直加到num>=k时,也就是连续k个的最长公共子串都>=t了。也就可以了。 




⑤不同子串的个数
子串等同于每个后缀的前缀。
对于每个后缀,我们可以知道相邻后缀有多少个公共前缀。
我们也可以秋池相邻后缀一共有多少个前缀。两者相减即可。


把后缀按照suffix(sa[i])进行排序后。
相邻的两个,后面的和他的上一个有height[k]个公共前缀。
而前缀有n-sa[k]+1个。
也就是产生了n-sa[k]+1-height[k]个不同的子串。
累加即可。




⑥最长回文子串
对于这个字符串我们复制一遍。
之后变成两个后缀的最长公共前缀。
需要注意的是两个后缀是有范围限制的。一个小于等于len,一个大于len






⑦连续重复子串:
给定一个L,已知这个L是有某个字符串S重复R次得到的,求R的最大值;
二分那个重复子串的长度k。
首先看看L%k是否等于零。
之后我们看suffix(1)从一开始的子串和fuffix(k+1)从第k的开始的子串 他们的最长公共前缀是否等于n-k;
suffix(1)我们是固定的,所有我们只需要求suffix(i)到suffix(1)的公共子串即可。


我们找到suffix(1)在rink中排第几。
之后正着和倒着求其他值和suffix(1)的公共串即可。




⑧重复次数最多的连续重复子串:
不太会




⑨两个字符串的最长公共子串
连接起来找最长公共前缀即可。只是要注意一个的开头<len,一个>=len


⑩长度不小于k的公共子串的个数。


11:不小于 k 个字符串中的最长子串
先连接起来,求height。
之后我们二分最长字串的长度L。
for遍历所有的height,如果当前height>=L,遍历1-n   j,也就是所有的字符串。
如果两个不是同一个的话,我们sum+1.之后标记那个找到的j,表示已经访问过了。
我们再继续找其他的。如果找到之后sum>k了,记录出发点,输出即可。




12:每个字符串至少出现两次且不重叠的最长子串
我们要统计每个后缀对于最长公共前缀出现的第一个和最后一个的位置。
之后遍历所有的后缀,如果他们公共前缀第一个和最后一个的查这个大于二分的值。
如果全部都大于,那么就是这个了。






//每个字符串至少出现两次且不重叠的最长子串
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <math.h>
#include <bitset>
#include <algorithm>
#include <climits>
using namespace std;


#define LS 2*i
#define RS 2*i+1
#define UP(i,x,y) for(i=x;i<=y;i++)
#define DOWN(i,x,y) for(i=x;i>=y;i--)
#define MEM(a,x) memset(a,x,sizeof(a))
#define W(a) while(a)
#define gcd(a,b) __gcd(a,b)
#define LL long long
#define N 100005
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define EXP 1e-8
int wa[N],wb[N],wsf[N],wv[N],sa[N];
int rank[N],height[N],s[N],a[N];
//sa:字典序中排第i位的起始位置在str中第sa[i]
//rank:就是str第i个位置的后缀是在字典序排第几
//height:字典序排i和i-1的后缀的最长公共前缀
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)//n要包含末尾添加的0
{
    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-1; i>=0; i--)  sa[--wsf[x[i]]]=i;
    p=1;
    j=1;
    for(; 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++)  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-1; 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 *r,int n)//n不保存最后的0
{
    int i,j,k=0;
    for(i=1; i<=n; i++)  rank[sa[i]]=i;
    for(i=0; i<n; i++)
    {
        if(k)
            k--;
        else
            k=0;
        j=sa[rank[i]-1];
        while(r[i+k]==r[j+k])
            k++;
        height[rank[i]]=k;
    }
}


char str[N];
int id[N],maxn[N],minn[N];


bool check(int mid,int n,int k)
{
    int i,j;
    for(i = 0; i<=k; i++)
    {
        maxn[i] = 0;
        minn[i] = INF;
    }
    for(i = 1; i<=n; i++)
    {
        if(height[i]<mid)
        {
            for(j = 0; j<=k; j++)
            {
                maxn[j] = 0;
                minn[j] = INF;
            }
            maxn[id[sa[i]]] = sa[i];
            minn[id[sa[i]]] = sa[i];
        }
        else
        {
            //求出第id[sa[i]]个串中,所使用头和尾,差值必须大于等于二分的答案
            maxn[id[sa[i]]] = max(maxn[id[sa[i]]],sa[i]);
            minn[id[sa[i]]] = min(minn[id[sa[i]]],sa[i]);
            maxn[id[sa[i-1]]] = max(maxn[id[sa[i-1]]],sa[i-1]);
            minn[id[sa[i-1]]] = min(minn[id[sa[i-1]]],sa[i-1]);
            for(j = 0; j<k; j++)
            {
                if(maxn[j]-minn[j]<mid)
                    break;
            }
            if(j==k) return true;
        }
    }
    return false;
}


int main()
{
    int t,n,i,j,k;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&k);
        n = 0;
        for(i = 0; i<k; i++)
        {
            scanf("%s",str+n);
            for(; str[n]!='\0'; n++)
            {
                s[n] = str[n];
                id[n] = i;
            }
            s[n++] = '#'+i;
        }
        s[n-1] = 0;
        getsa(s,sa,n,255);
        getheight(s,n-1);
        int l = 0,r = 10000,mid,ans = 0;
        while(l<=r)
        {
            mid = (l+r)/2;
            if(check(mid,n,k))
            {
                ans = mid;
                l = mid+1;
            }
            else r = mid-1;
        }
        printf("%d\n",ans);
    }


    return 0;
}


//
连续重复子串
void iniRMQ(int len)  
{  
    int to = Rank[0];  
    rm[to] = maxn;  
    for(int i = to - 1;i >= 0;i--)  
    {  
        if(height[i+1] < rm[i+1]) rm[i] = height[i+1];  
        else rm[i] = rm[i+1];  
    }  
  
    for(int i = to+1;i <= len;i++)  
    {  
        if(height[i] < rm[i-1]) rm[i] = height[i];  
        else rm[i] = rm[i-1];  
    }  
}  




//求不同子串的数量 
#include <cstdio>
#include <algorithm>
using namespace std;


int const SIZE = 1005;
//分隔符,多串连接时需要用到,第0个为结束符,肯定用到
char const DELIMETER[] = {'#'};
int const DELIMETER_CNT = 1;
//字母表的字母个数
int const ALPHA_SIZE = DELIMETER_CNT + 128;
//char转int
inline int tr(char ch){
    if ( DELIMETER[0] == ch ) return 0;
    return ch;
}
//辅助数组,以下划线开头
int _wa[SIZE],_wb[SIZE],_wv[SIZE],_ws[SIZE];
//辅助函数
int _cmp(int const r[],int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
//求后缀数组的倍增算法
//r: 源数组,且除r[n-1]外,其余r[i]>0
//n: r的长度
//m: r中的元素取值的上界,即任意r[i]<m
//sa:后缀数组,即结果
void da(int const r[],int n,int m,int sa[]){
    int i,j,p,*x=_wa,*y=_wb,*t;
    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(t=x,x=y,y=t,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++;
    }
    return;
}


//计算rank数组与height数组
//r:  源数组
//sa: 后缀数组
//n:  源数组的长度
//rank: rank数组,即计算结果
//height: height数组,即计算结果
void calHeight(int const r[],int const sa[],int n,int rank[],int height[]){
    int i,j,k=0;
    for(i=1;i<n;i++) rank[sa[i]]=i;
    for(i=0;i<n-1;height[rank[i++]]=k)
    for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
    return;
}


void dispArray(int const a[],int n){
    for(int i=0;i<n;++i)printf("%d ",a[i]);
    printf("\n");
}


int R[SIZE];
int SA[SIZE],Rank[SIZE],Height[SIZE];
int N,K;
char A[SIZE];
bool read(){
    scanf("%s",A);
    for(N=0;A[N];++N) R[N] = tr(A[N]);
    R[N++] = 0;
    return true;
}


int proc(){
    da(R,N,ALPHA_SIZE,SA);
    calHeight(R,SA,N,Rank,Height);


    /*
    dispArray(R,N);
    dispArray(SA,N);
    dispArray(Rank,N);
    dispArray(Height,N);
    //*/


    //查找不同的子串数量,即查找不同的前缀数量
    //每个后缀可以带入N-SA[i]个前缀,其中相同的有Height[i]个
    //最后的结束标记会带入N个,不应计入答案
    int r = -N;
    for(int i=0;i<N;++i) r += N - SA[i] - Height[i];
    return r;
}


int main(){
    int nofkase;
    scanf("%d",&nofkase);
    while( nofkase-- ){
        read();
        printf("%d\n",proc());
    }
    return 0;
}