hdu3518(后缀数组求至少出现两次以上的不可重叠串)

本文介绍了一种使用后缀数组解决字符串问题的方法,即找出一个长度为n的字符串中出现2次及以上且不重叠的子串数量。通过构建后缀数组sa[]和rank[],利用height数组优化查找过程。

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

一段时间没学后缀数组,刚开始练sa[]和rank[]的含义都忘记了。
题意:给一个长度为n的字符串,问有多少个字符串至少出现2次以上且不重叠。

思路:后缀数组解,求后缀数组的时间复杂度nlogn,求答案的时间复杂度为n*n,枚举字符串长度为i个串,利用height数组,求某个连续区间(height[i] >= i)中排在最左边和左右边的sa,然后两个的差值如果大于等于i的话,ans++。

代码如下:

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

#define N 10005
using namespace std;

char s[N];
int r[N];
int wa[N],wb[N],wv[N],WS[N],sa[N];
//sa[i]表示排在第i个的字符串是后缀 sa[i]
//rank[i]表示后缀i的排名
int cmp(int *r,int a,int b,int l)
{
    return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int n,int m)
{
    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++;
    }
}
int rank[N],height[N];
void calheight(int n)
{
    int i,j,k = 0;
    for(i = 1; i <= n; i++) rank[ sa[i] ] = i;
    for(i = 0; i < n; height[ rank[i++] ] = k)
    for(k ? k--:0,j = sa[ rank[i]-1 ]; r[i+k] == r[j+k]; k++);
}
int main()
{
    while(cin>>s && s[0] != '#')
    {
        int n = strlen(s);
        int i;
        for(i = 0; i < n; i++)
            r[i] = s[i] - 'a' + 1;
        r[n] = 0;
        da(n+1,27);
        calheight(n);
        height[n+1] = 0;

        long long  ans = 0 ;
        for(i = 1; i <= (n+1)/2; i++)
        {
            int l = N,r = - N;
            for(int j = 0; j <= n+1; j++)
            {
                if(height[j] >= i)
                {
                    l = min(l,sa[j]);
                    l = min(l,sa[j-1]);
                    r = max(r,sa[j]);
                    r = max(r,sa[j-1]);
                }
                else
                {
                    if(l != N && r - l >= i) ans++;
                    l = N;
                    r = -N;
                }
            }
        }
    //printf("%I64d\n",ans);
    cout<<ans<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值