hdu 4691 最长的共同前缀 后缀数组 +lcp+rmq

http://acm.hdu.edu.cn/showproblem.php?

pid=4691

去年夏天,更多的学校的种族称号。当时,没有后缀数组

今天将是,事实上,自己的后缀阵列组合rmq或到,但是,题意理解的一个问题,再折腾了很长时间,,,,

此处简单解释下题目例子吧,希望对读者有帮助  以最后一组数据为例

myxophytamyxopodnabnabbednabbingnabit

6

0 9

9 16

16 19

19 25

25 32

32 37

前两行不解释,题目叙述非常清楚

从第三行,0 9 指的是第一个字符串是从第一行的字符串的0-9 左闭右开,

下面5行同样

继续看题目的正文叙述的例子

那么压缩之后的第一个就是“0空格以及前9个字符外加一个换行”一共12个。下面几行相同的算法

注意假设公共前缀长度是24,那么按两个单元存储,这就是我写的Weishu函数的作用

上代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cmath>

using namespace std;

const int MAXN =100000+20;

int n,k;//n=strlen(s);
int Rank[MAXN],tmp[MAXN],d[MAXN],st[MAXN][20],lcp[MAXN],sa[MAXN];
char s[MAXN];

/*使用Rank对sa排序*/
bool cmpSa(int i, int j)
{
    if(Rank[i] != Rank[j])return Rank[i] < Rank[j];
    else
    {   /*以下的Rank[t],已经是以t开头长度小于等于k/2的。
        sa[i]的名次,仅仅是以i开头的后缀,而长度不同*/
        int ri = i+k <=n? Rank[i+k]:-1;
        int rj = j+k <= n ? Rank[j+k]:-1;
        return ri <rj;
    }
}

/*计算SA*/
void consa(char *s, int *sa)
{
    for(int i=0;i<=n;i++){
        sa[i]=i;Rank[i] = i < n?s[i]:-1;
    }

    for(k=1;k<=n;k*=2)/*注意此代码中k是全局变量 别乱用,循环必须从1開始,由于0*2=0*/
    {
        sort(sa,sa+n+1,cmpSa);
        tmp[sa[0]] = 0; /*此时tmp仅仅是暂存rank*/
        for(int i=1;i<=n;i++){
            tmp[sa[i]] = tmp[sa[i-1]] +(cmpSa(sa[i-1],sa[i])?1:0);
        }
        for(int i=0;i<=n;i++){
            Rank[i] = tmp[i];
        }
    }
}

void construct_lcp(char *s,int *sa,int *lcp)
{
    //n=strlen(s);
    for(int i=0; i<=n; i++)Rank[sa[i]]=i;

    int h=0;
    lcp[0]=0;
    for(int i=0;i<n;i++)
    {
        int j=sa[Rank[i]-1];

        if(h>0)h--;
        for(; j+h<n && i+h<n; h++)
        {
            if(s[j+h]!=s[i+h])break;
        }
        lcp[Rank[i]-1]=h;
    }
}

void InitRMQ(int nn)
{
    int i,j;
    for(d[0]=1,i=1;i<21;i++)d[i]=2*d[i-1];
    for(i=0;i<nn;i++)st[i][0]=lcp[i];
    ///////////////////////////////
   // for(int i=0;i<nn;i++)
   //{
  //      printf("%s i=%d sa=%d rank=%d lcp=%d\n",s+sa[i],i,sa[i],Rank[i],lcp[i]);
  //      printf("||||||||%s lcp[rank]=%d\n",s+i,lcp[Rank[i]]);
  //  }
    ////////////////////////////////

    int k=int( log(double(nn))/log(2.0)+1 );
    for(j=1;j<k;j++)
        for(i=0;i<nn;i++)
        {
            if(i+d[j-1]-1<nn)
            {
                st[i][j]=min(st[i][j-1],st[i+d[j-1]][j-1]);
            }
            else break;
        }
}

int weishu(int n)
{
    if(n<10)return 1;
    int ans=0;
    while(n)
    {
        n/=10;
        ans++;
    }
    return ans;
}

int main()
{
    //freopen("hdu4691.txt","r",stdin);
    long long ansb,ansa;
    int kk;
    while(~scanf("%s",s))
    {
        ansb=ansa=0;
        n=strlen(s);
        consa(s,sa);
        construct_lcp(s,sa,lcp);

        InitRMQ(n+1);
        int num,l,r,x,y;
        scanf("%d",&num);
        int last=0,lastlen=0;
        scanf("%d%d",&l,&r);
        ansb+=r-l+1;
        ansa+=r-l+3;
        lastlen=r-l;
        last=l;
        for(int i=1;i<num;i++)
        {
            scanf("%d%d",&l,&r);
            ansb+=r-l+1;
            ansa+=r-l;
            x=min(Rank[last],Rank[l]),y=max(Rank[last],Rank[l])-1;
            kk = int( log(double(y-x+1))/log(2.0) );
            int ret;
            if(l == last)ret=n-l;
            else ret=min(st[x][kk], st[y-d[kk]+1][kk]) ;
            ret=min(ret, min(lastlen,r-l));
            ansa =ansa-ret+weishu(ret)+2;
            last=l;
            lastlen=r-l;
        }
        printf("%I64d %I64d\n",ansb,ansa);
    }
    return 0;
}

再加一个rmq+后缀数组求最长公共前缀的模板吧

(事实上还没有測试,遇到题在測试)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cmath>

using namespace std;

const int MAXN =100000+20;

int n,k;//n=strlen(s);
int Rank[MAXN],tmp[MAXN],d[MAXN],st[MAXN][20],lcp[MAXN],sa[MAXN];
char s[MAXN];

/*使用Rank对sa排序*/
bool cmpSa(int i, int j)
{
    if(Rank[i] != Rank[j])return Rank[i] < Rank[j];
    else
    {   /*以下的Rank[t],已经是以t开头长度小于等于k/2的,
        sa[i]的名次。仅仅是以i开头的后缀,而长度不同*/
        int ri = i+k <=n? Rank[i+k]:-1;
        int rj = j+k <= n ? Rank[j+k]:-1;
        return ri <rj;
    }
}

/*计算SA*/
void consa(char *s, int *sa)
{
    for(int i=0;i<=n;i++){
        sa[i]=i;Rank[i] = i < n?s[i]:-1;
    }

    for(k=1;k<=n;k*=2)/*注意此代码中k是全局变量 别乱用,循环必须从1開始。由于0*2=0*/
    {
        sort(sa,sa+n+1,cmpSa);
        tmp[sa[0]] = 0; /*此时tmp仅仅是暂存rank*/
        for(int i=1;i<=n;i++){
            tmp[sa[i]] = tmp[sa[i-1]] +(cmpSa(sa[i-1],sa[i])?1:0);
        }
        for(int i=0;i<=n;i++){
            Rank[i] = tmp[i];
        }
    }
}

void construct_lcp(char *s,int *sa,int *lcp)
{
    //n=strlen(s);
    for(int i=0; i<=n; i++)Rank[sa[i]]=i;

    int h=0;
    lcp[0]=0;
    for(int i=0;i<n;i++)
    {
        int j=sa[Rank[i]-1];

        if(h>0)h--;
        for(; j+h<n && i+h<n; h++)
        {
            if(s[j+h]!=s[i+h])break;
        }
        lcp[Rank[i]-1]=h;
    }
}

void InitRMQ(int nn)
{
    int i,j;
    for(d[0]=1,i=1;i<21;i++)d[i]=2*d[i-1];
    for(i=0;i<nn;i++)st[i][0]=lcp[i];
    int k=int( log(double(nn))/log(2.0)+1 );
    for(j=1;j<k;j++)
        for(i=0;i<nn;i++)
        {
            if(i+d[j-1]-1<nn)
            {
                st[i][j]=min(st[i][j-1],st[i+d[j-1]][j-1]);
            }
            else break;
        }
}

int ansLcp(int a, int b)
{
    int kk;
    if(a == b)return n-a;//n是整个字符串的长度
    x=min(Rank[a],Rank[b]),y=max(Rank[a],Rank[b])-1;
    kk = int( log(double(y-x+1))/log(2.0) );
    return min(st[x][kk], st[y-d[kk]+1][kk]) ;
}

int Query(int Q)//Q次询问
{
    int ans;
    for(int i=0;i<Q;i++)
    {
        scanf("%d%d",&a,&b);
        ans=ansLcp(a,b);
    }
}

版权声明:本文博客原创文章,博客,未经同意,不得转载。

时间: 2024-10-21 17:23:15

hdu 4691 最长的共同前缀 后缀数组 +lcp+rmq的相关文章

hdu 4691 最长公共前缀 后缀数组 +lcp+rmq

http://acm.hdu.edu.cn/showproblem.php?pid=4691 去年暑假多校赛的题,当时还不会后缀数组 现在会了,其实自己组合后缀数组跟rmq还是对的,但是题意理解有问题,于是折腾了很久,,,, 此处简单解释下题目样例吧,希望对读者有帮助  以最后一组数据为例 myxophytamyxopodnabnabbednabbingnabit 6 0 9 9 16 16 19 19 25 25 32 32 37 前两行不解释,题目叙述很清楚 从第三行,0 9 指的是第一个字

poj 1743 最长不重叠重复子串 后缀数组+lcp+二分

题比较容易读懂,但是建模需动点脑子: 一个子串加常数形成的子串认为跟子串相同,求最长不重叠重复子串 题目中说 is disjoint from (i.e., non-overlapping with) at least one of its other appearance(s) 意味着不能重叠,举个例子 1, 2,3,  52, 53,54 1,2, 3和 52, 53,54满足题意,差值为51 枚举差值肯定不行------看了题解明白的:: 后项减去前一项得到: 1,1,1,49,1,1  

poj 2774 Long Long Message 后缀数组LCP理解

题目链接 题意:给两个长度不超过1e5的字符串,问两个字符串的连续公共子串最大长度为多少? 思路:两个字符串连接之后直接后缀数组+LCP,在height中找出max同时满足一左一右即可: #include<iostream> #include<cstdio> #include<cstring> #include<string.h> #include<algorithm> #include<map> #include<queue&

URAL 1297. Palindrome(输出最长回文子串--后缀数组)

Input The input consists of a single line, which contains a string of Latin alphabet letters (no other characters will appear in the string). String length will not exceed 1000 characters. Output The longest substring with mentioned property. If ther

hdu 3518 Boring counting 后缀数组LCP

题目链接 题意:给定长度为n(n <= 1000)的只含小写字母的字符串,问字符串子串不重叠出现最少两次的不同子串个数; input: aaaa ababcabb aaaaaa # output 2 3 3 思路:套用后缀数组求解出sa数组和height数组,之后枚举后缀的公共前缀长度i,由于不能重叠,所以计数的是相邻height不满足LCP >= i的. 写写对后缀数组倍增算法的理解: 1.如果要sa数组对应的值也是1~n就需要在最后加上一个最小的且不出现的字符'#',里面y[]是利用sa数

[后缀数组+二分+rmq] hdu 5008 Boring String Problem

有点小可惜这道题,当时整个思路都想到了,就是最后找最左下标的时候不会处理, 然后结束完发现直接暴力就可以了,想到了可是不敢写,10w个a直接就T了啊... 数据太弱了,敢写就过系列啊 T T. 然后希望有大神提供完美思路! 题意: 给一个字符串 然后n次询问 对于每一次询问给一个v 然后问第 l⊕r⊕v+1小的子串的区间   (⊕代表异或) 然后输出l r 这里的l r 就是上一次输出的l r  初始化是0 0 不存在输出0 0  如果多个 输出出现最早的. 思路: 首先后缀数组就不说了,做完之

HDU - 5008 Boring String Problem (后缀数组+二分+RMQ)

Problem Description In this problem, you are given a string s and q queries. For each query, you should answer that when all distinct substrings of string s were sorted lexicographically, which one is the k-th smallest. A substring si...j of the stri

POJ 2774 Long Long Message(最长公共子串 -初学后缀数组)

后缀数组的两篇神论文: 国家集训队2004论文集 许智磊 算法合集之<后缀数组--处理字符串的有力工具> 很多人的模版都是用论文上的 包括kuangbin的模版:(DA算法) 模版中比较难理解的地方有两点1.按关键词排序 2.把字符串长度增加一位 按关键词排序的意思其实是基数排序中相当把两位数排序时先排个位,再排十位 这里也一样先排后2^k长度的字符串,再排前2^k长度的字符串,最终排成2^(k+1)字符长度的后缀数组sa 把字符串增加一位,是为了让有意义的串的rank从1开始,还有便于后边不

后缀数组LCP + 二分 - UVa 11107 Life Forms

Life Forms Problem's Link Mean: 给你n个串,让你找出出现次数大于n/2的最长公共子串.如果有多个,按字典序排列输出. analyse: 经典题. 直接二分判断答案. 判断答案p时,我们扫一遍height数组,如果height[i]<p时开辟一个新段. 判断时用set存储所在串编号,不仅起到去重的作用,而且也起到统计段长的作用. Time complexity: O(N*logN) Source code:  /** this code is made by cra