HDU 4416 Good Article Good sentence(后缀数组 求在只在某串中出现的子串数目)

本文详细介绍了如何使用后缀数组和高度数组解决多个字符串的子串问题,包括字符串拼接、处理特殊字符和计算不同子串的数量。通过实例演示了算法思想和关键步骤,适合对后缀数组应用感兴趣的读者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4416


这个题目我感觉不好写,各种出错,最后参考这篇博客的程序和思想才得以完成:http://hi.baidu.com/arosliu/item/ec421c23bf862ef951fd8769

开始按照以前的后缀数组的写法,老是Run time error 后来改成上面链接的后缀数组写法就没问题了,目前还不知道问题出在什么地方

这个题目的思想我觉得非常巧妙,很好的利用了后缀数组和height数组的性质

首先处理这种多个字符串问题一般要是用后缀数组来处理的话就是字符串拼接,拼接的同时在每个串之间插入一个独一无二的字符

所以这里在处理的时候把字符全部转换成整形数,原因是如果用字符,哪来那么多独一无二的字符呢

然后拼接完成之后求sa rank 和height,然后求出这个数组的所有不用子串的数目,这个数目还是比较好求的

求完之后减去子串中包含特殊字符(我们拼接时候添加的)的子串数目,现在得出的结果就是不包含特殊字符的不同子串的个数

实际上就所有串中不同子串的数目(这里好好理解下),因为前面加后面的总共是len_ab,后面的是len_b,结果肯定是相减嘛

同样的做法求出后面,也就是不包含第一个串的这个值,然后两个相减就得到了结果!

至于怎样求解包含特殊字符的不同字串个数呢,其实就是下面这段代码

 for (i = 0; i < number; i++)
        {
            l -= L[i];
            len_ab-= (L[i]+1)*l;
            l--;
        }

L[i]表示第i个字符串的长度,L初始值等于len-1;

每次都这样减去,为什么这样是正确的,因为包含了这个特殊字符,那么不同的子串个数就是个组合,前面取连续的不同的L[i]+1个,后面连续的

不同的l个,两者相乘,当然这个只是包含第i个特殊字符的情况,随着i++的进行,结果就出来了!

/*4416*/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#include <string.h>
using namespace std;
#define maxn 310000
int top[maxn],tmp[maxn];
int len;
char rec[maxn];
int rec1[maxn];
int L[maxn];
int sa[maxn];
int rank[maxn],height[maxn];
int *str;
void makesa()
{
    int lena = len < 256 ? 256 : len;
    memset(top, 0, lena*sizeof(int));
    for (int i = 0; i < len; i++)
        top[rank[i] = str[i]&(-1)]++;
    for (int i = 1; i < lena; i++)
        top[i] += top[i-1];
    for (int i = 0; i < len ; i++)
        sa[--top[rank[i]]] = i;
    for (int k = 1; k < len; k <<= 1)
    {
        for (int i = 0; i < len; i++)
        {
            int j = sa[i]-k;
            if (j < 0)
                j += len;
            tmp[top[rank[j]]++] = j;
        }
        int j = sa[tmp[0]] = top[0] = 0;
        for (int i = 1; i < len; i++)
        {
            if (rank[tmp[i]] != rank[tmp[i-1]] || rank[tmp[i]+k] != rank[tmp[i-1]+k])
                top[++j] = i;
            sa[tmp[i]] = j;
        }
        memcpy(rank, sa , len*sizeof(int));
        memcpy(sa , tmp, len*sizeof(int));
        if (j+1 >= len)
            break;
    }
}
void lcp()
{
    height[0] = 0;
    for (int i = 0, k = 0, j = rank[0]; i+1 < len; i++, k++)
        while (k >= 0 && str[i] != str[sa[j-1]+k])
        {
            height[j] = k--;
            j = rank[sa[j]+1];
        }
}
int main()
{
    int i,j,k=0;
    int left,right;
    int mid,number,ans,ans1,len_f;
    long long  len_ab,len_b,l;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        len_ab=len_b=0;
        scanf("%d",&number);
        scanf("%s",rec);
        for(len=0;rec[len];len++)
        {
            rec1[len]=rec[len]-'a'+1;
        }
        L[0]=len;
        for(i=1;i<=number;i++)
        {
            rec1[len++]=26+i;
            scanf("%s",rec);
            for(j=0;rec[j];j++)
            rec1[len++]=rec[j]-'a'+1;
            L[i]=j;
        }
        rec1[len++]=0;
        str=rec1;
        makesa();
        lcp();
          for ( i = 1; i < len; i++)
            len_ab += len-1-sa[i]-height[i];
        l=len-1;
        for (i = 0; i < number; i++)
        {
            l -= L[i];
            len_ab-= (L[i]+1)*l;
            l--;
        }
        len-=L[0]+1;
        str=rec1+L[0]+1;
        makesa();
        lcp();
        for(i = 1; i < len; i++)
            len_b += len-1-sa[i]-height[i];
        l=len-1;
        for (i = 1; i < number; i++)
        {
            l -= L[i];
            len_b-= (L[i]+1)*l;
            l--;
        }
        cout<<"Case "<<++k<<": "<<len_ab-len_b<<endl;
        }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值