hdu-4691 最长公共前缀-后缀数组

http://acm.hdu.edu.cn/showproblem.php?pid=4691

http://blog.csdn.net/fire_cat11211022/article/details/9908545  详细的后缀数组有关问题的解答


解析:当然用后缀数组最方便,在后缀数组中有很多重要的定义和性质,现在我们来认识一些:

定义:LCP(i,j)=suffix(SA[i])与suffix[SA[j]]的最长公共前缀长度,即排号序后的后缀中第i名和第j名的最长公共前缀长度。

然后我们再用一个重要的性质就可以求出LCP(i,j)了,性质描述:LCP(i,j)=min{LCP(k-1,k)}  i

而对于LCP(k-1,k)我们有height数组啊,比如我们要求suffix(i)和suffix(j)的最长公共前缀,就相当于求height[rank[i]]到height[rank[j]]之间的最小值。

至于区间求最小值,线段树是个很好的选择,到此问题圆满解决。


#include //sort
#include //memset
#include 
#include 
#define MAX 2000100
using namespace std;
const int MAX_SFX = 210000;
struct Sfx
{
    int i;
    int key[2];
    bool operator < (const Sfx& s) const
    {
        return key[0] < s.key[0]
               || key[0] == s.key[0] && key[1] < s.key[1];
    }
};

typedef struct
{
    int l;
    int r;
    int minn;
} Tree;
Tree tree[MAX*4];
int build(int i,int l,int r) //建树
{
    int mid;
    tree[i].l=l;
    tree[i].r=r;
    tree[i].minn=MAX;
    if(l==r)
    {
        return 0;
    }
    mid=(l+r)/2;
    build(i*2,l,mid);
    build(i*2+1,mid+1,r);
}
int insert(int i,int k,int v) //插入
{
    tree[i].minn=v < tree[i].minn? v:tree[i].minn;
    if(tree[i].l==tree[i].r)
        return 0;
    else
    {
        int mid=(tree[i].l+tree[i].r)/2;
        if(mid=tree[i].r))
    {
        return tree[i].minn;
    }
    ans=MAX;
    mid=(tree[i].l+tree[i].r)/2;
    if (x<=mid)  ans=min(ans,findmin(i*2,x,y));
    if (y>mid)   ans=min(ans,findmin(i*2+1,x,y));
    return ans;
}
int g_buf[MAX_SFX + 1];
Sfx g_tempSfx[2][MAX_SFX], *g_sa = g_tempSfx[0];
void cSort(Sfx* in, int n, int key, Sfx* out)
{
    int* cnt = g_buf;
    memset( cnt, 0, sizeof(int) * (n + 1) );
    for (int i = 0; i < n; i++)
    {
        cnt[ in[i].key[key] ]++;
    }
    for (int i = 1; i <= n; i++)
    {
        cnt[i] += cnt[i - 1];
    }
    for (int i = n - 1; i >= 0; i--)
    {
        out[ --cnt[ in[i].key[key] ] ] = in[i];
    }
}
void buildSA(char* text, int len)
{
    Sfx *temp = g_tempSfx[1];
    int* rank = g_buf;
    for (int i = 0; i < len; i++)
    {
        g_sa[i].i = g_sa[i].key[1] = i;
        g_sa[i].key[0] = text[i];
    }
    sort(g_sa, g_sa + len);
    for (int i = 0; i < len; i++)
    {
        g_sa[i].key[1] = 0;
    }
    int wid = 1;
    while (wid < len)
    {
        rank[ g_sa[0].i ] = 1;
        for (int i = 1; i < len; i++)
        {
            rank[ g_sa[i].i ] = rank[ g_sa[i - 1].i ];
            if ( g_sa[i-1] < g_sa[i] )
            {
                rank[ g_sa[i].i ]++;
            }
        }
        for (int i = 0; i < len; i++)
        {
            g_sa[i].i = i;
            g_sa[i].key[0] = rank[i];
            g_sa[i].key[1] = i + wid < len? rank[i + wid]: 0;
        }
        cSort(g_sa, len, 1, temp);
        cSort(temp, len, 0, g_sa);
        wid *= 2;
    }
}

int getLCP(char* a, char* b)
{
    int l=0;
    while(*a && *b && *a==*b)
    {
        l++;
        a++;
        b++;
    }
    return l;
}

void getLCP(char* text, Sfx* sfx, int len, int* lcp)
{
    int* rank = g_buf;
    for (int i=0, r=0; i < len; i++, r++)
    {
        rank[ sfx[i].i ] = r;
    }
    lcp[0] = 0;
    if (rank[0])
    {
        lcp[ rank[0] ] = getLCP( text, text + sfx[ rank[0]-1 ].i );
    }
    for (int i = 1; i < len; i++)
    {
        if ( !rank[i] )
        {
            continue;
        }
        if (lcp[ rank[i - 1] ] <= 1)
        {
            lcp[ rank[i] ] = getLCP( text+i, text+sfx[ rank[i]-1 ].i );
        }
        else
        {
            int L = lcp[ rank[i - 1] ] - 1;
            lcp[rank[i]] = L+getLCP(text+i+L, text+sfx[rank[i]-1].i+L);
        }
    }
}
int rank[100010];
char str[100010];

int main()
{
    while(~scanf("%s",str))
    {
        int lcp[100010];
        int len=strlen(str);
        buildSA(str,len);    //获取sa数组
        getLCP(str,g_sa,len, lcp); //获取height[]即lcp[]  g_buf即 rank[];
        build(1,0,len-1);
        for(int i=0; ig_buf[lx])  //比较rank的大小  决定起始位置
                    sum=findmin(1,g_buf[lx]+1,g_buf[x]);
                else
                    sum=findmin(1,g_buf[x]+1,g_buf[lx]);
                sum=min(sum,min(y-x,ly-lx));//三个长度的最小
            }
            ans2-=sum;
            if(sum==0)  //注意等于0时也是1位
                sum1++;
            else
            {
                while(sum)
                {
                    sum/=10;
                    sum1++;
                }
            }
            ans2=ans2+sum1+y-x+2;
            ly=y; lx=x;
        }
        printf("%I64d %I64d\n",ans1,ans2);
    }
    return 0;
}


你可能感兴趣的:(hdu,线段树,后缀数组)