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;
}

hdu 4691 最长公共前缀 后缀数组 +lcp+rmq,布布扣,bubuko.com

时间: 2024-12-19 05:51:37

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 前两行不解释,题目叙述非常清楚 从第三行

POJ 2774 求两个串的最长公共前缀 | 后缀数组

#include<cstdio> #include<algorithm> #include<cstring> #define N 200005 using namespace std; int buf1[N],buf2[N],sa[N],rnk[N],buc[N],n,height[N],ans,belong[N]; char s[N]; void suffix_sort() { int *x=buf1,*y=buf2,m=1000; for (int i=0;i<

HDU 1403 Longest Common Substring(后缀数组,最长公共子串)

hdu题目 poj题目 参考了 罗穗骞的论文<后缀数组——处理字符串的有力工具> 题意:求两个序列的最长公共子串 思路:后缀数组经典题目之一(模版题) //后缀数组sa:将s的n个后缀从小到大排序后将 排序后的后缀的开头位置 顺次放入sa中,则sa[i]储存的是排第i大的后缀的开头位置.简单的记忆就是“排第几的是谁”. //名次数组rank:rank[i]保存的是suffix(i){后缀}在所有后缀中从小到大排列的名次.则 若 sa[i]=j,则 rank[j]=i.简单的记忆就是“你排第几”

HDU 1403 Longest Common Substring(后缀数组啊 求最长公共子串 模板题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1403 Problem Description Given two strings, you have to tell the length of the Longest Common Substring of them. For example: str1 = banana str2 = cianaic So the Longest Common Substring is "ana", a

后缀数组之最长公共前缀

#include<stdio.h> #define maxn 100 int main() { int rank[maxn],height[maxn],sa[maxn]= {0,3,1,4,2},s[maxn]= {1,2,3,2,3};//s串可以看成abcbc int i,j,k=0; for(i=0; i<5; i++) rank[sa[i]]=i; for(i=0; i<5; i++) { if(rank[i]==0)//当rank[i]=0的时候也就是求height[0]

CSU1632Repeated Substrings(后缀数组/最长公共前缀)

题意就是求一个字符串的重复出现(出现次数>=2)的不同子串的个数. 标准解法是后缀数组.最长公共前缀的应用,对于样例aabaab,先将所有后缀排序: aab 3    aabaab 1    ab 2    abaab 0    b 1    baab 每个后缀前面数字代表这个后缀与它之前的后缀(rank比它小1)的最长公共前缀的长度:然而就可以这样理解这个最长公共前缀LCP.aabaab与aab的最长公共前缀是3,那说明子串a.aa.aab都至少出现的两次,那么这就是后缀aab重复出现的子串个

LeetCode -- 求字符串数组中的最长公共前缀

题目描写叙述: Write a function to find the longest common prefix string amongst an array of strings.就是给定1个字符串数组,找出公共最长前缀. 思路非常直接.使用1个索引来存最长公共前缀的长度就能够了. 注意, 假设使用1个字符串变量来存前缀的话,是不能AC的,由于题目不同意使用额外的空间. public string LongestCommonPrefix(string[] strs) { if(strs

leetcode——Longest Common Prefix 最长公共前缀(AC)

Write a function to find the longest common prefix string amongst an array of strings. 其实做起来会感觉很简单,需要注意的是要考虑效率的问题,毕竟可能是很长的字符串数组,所以可以考虑选取所有字符串中最短的那个来首先进行比较,因为最长公共子串肯定不会大于其长度,这样避免了字符串之间长度差异很大造成的效率损失,然后每次比较之后最长公共子串的长度也永远不会大于最短的那个字符串,只会不变或减小,只要遍历字符串数组,挨个

【LeetCode-面试算法经典-Java实现】【014-Longest Common Prefix(最长公共前缀)】

[014-Longest Common Prefix(最长公共前缀)] [LeetCode-面试算法经典-Java实现][所有题目目录索引] 原题 Write a function to find the longest common prefix string amongst an array of strings. 题目大意 写一个函数找出一个字串所数组中的最长的公共前缀. 解题思路 第一步先找出长度最小的字符串,然后将这个字符串与其它的字符串相比找出最短的最公共前缀. 代码实现 publi