hdu6194 string string string sam或sa

题意:给你一个字符串,问这个字符串中有多少个子串出现了k次

上一篇的简化版。。
不过这个人的代码很清晰
参考http://www.cnblogs.com/weeping/p/7503972.html

据说sam的指针版会被卡。。看来还是要各种版本都会写才可以。。

#include <bits/stdc++.h>

using namespace std;

char ss[100004];
int ans;

struct SAM
{
    static const int MAXN = 100001<<1;//大小为字符串长度两倍
    static const int LetterSize = 26;

    int tot, last, ch[MAXN][LetterSize], fa[MAXN], len[MAXN];
    int sum[MAXN], tp[MAXN], cnt[MAXN]; //sum,tp用于拓扑排序,tp为排序后的数组

    void init( void)
    {
        last = tot = 1;
        len[1] = 0;
        memset(ch,0,sizeof ch);
        memset(fa,0,sizeof fa);
        memset(cnt,0,sizeof cnt);
    }

    void add( int x)
    {
        int p = last, np = last = ++tot;
        len[np] = len[p] + 1, cnt[last] = 1;
        while( p && !ch[p][x]) ch[p][x] = np, p = fa[p];
        if( p == 0)
            fa[np] = 1;
        else
        {
            int q = ch[p][x];
            if( len[q] == len[p] + 1)
                fa[np] = q;
            else
            {
                int nq = ++tot;
                memcpy( ch[nq], ch[q], sizeof ch[q]);
                len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq;
                while( p && ch[p][x] == q)  ch[p][x] = nq, p = fa[p];
            }
        }
    }

    void toposort( void)
    {
        for(int i = 1; i <= len[last]; i++)   sum[i] = 0;
        for(int i = 1; i <= tot; i++)   sum[len[i]]++;
        for(int i = 1; i <= len[last]; i++)   sum[i] += sum[i-1];
        for(int i = 1; i <= tot; i++)   tp[sum[len[i]]--] = i;
    }
} sam;


int main(void)
{
    //freopen("in.acm","r",stdin);
    int t,k;cin>>t;
    while(t--)
    {
        ans=0;
        sam.init();
        scanf("%d%s",&k,ss);
        for(int i=0,len=strlen(ss);i<len;i++)  sam.add(ss[i]-'a');
        sam.toposort();
        for(int i=sam.tot;i;i--)
        {
            int p=sam.tp[i],fp=sam.fa[p];
            sam.cnt[fp]+=sam.cnt[p];
            if(sam.cnt[p]==k)
                ans+=sam.len[p]-sam.len[fp];
        }
        printf("%d\n",ans);
    }

    return 0;
}

sa 的做法:

参考http://www.cnblogs.com/quintessence/p/7507923.html

只需求出出现次数>=k和 >=k+1的个数。

对于求出现次数>=x的,如果x=1,则直接特判输出。
若不然,只需维护长度x-1的height序列(height序列长度x-1 则对应的后缀数即为x),维护序列中height的最小值,按下标从小到大扫,注意到每次更新,只有该最小值发生变大时,才会增加新的出现次数>=x次的子串。(不变或变小时当前的子串之前都计数过)
以此计算>=x >=x+1的相减即可。

感悟:
这种“只有当height变大的时候才更新的方式好像也是挺普遍的。”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值