2017沈阳网络赛 1001 HDU 6194 string string string(后缀自动机 出现k次的子串个数)

题意:给你一个字符串,和一个k,问你字符串中恰好出现k次的子串有多少种。


思路:HDU4641的简化版,HDU4641求的是>=k次的子串个数,所以现在我们只需跑两次计算出>=k次的和>=k+1次的,两者相减即位恰好出现k次的子串个数。


具体思路参考HDU4641的就行:点击打开链接


代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+5;

int root,last;
int tots;
int l;

int sv[maxn*2];
struct query
{
    int a;
    ll ans;
}qu[maxn];

struct sam_node
{
    int fa,son[26];
    int len;
    void init(int _len)
    {
        len = _len;
        fa = -1;
        memset(son,-1,sizeof(son));
    }
}t[maxn*2];

void sam_init()
{
    tots = 0;
    root = last = 0;
    t[tots].init(0);
}

void extend(int w)
{
    int p = last;
    int np = ++tots;
    t[tots].init(t[p].len+1);
    sv[l] = np;
    int q, nq;
    while(p != -1 && t[p].son[w] == -1)
    {
        t[p].son[w] = np;
        p = t[p].fa;
    }
    if(p == -1) t[np].fa = root;
    else
    {
        q = t[p].son[w];
        if (t[p].len+1 == t[q].len) t[np].fa=q;
        else
        {
            nq = ++tots;
            t[nq].init(0);
            t[nq] = t[q];
            t[nq].len = t[p].len+1;
            t[q].fa = nq;
            t[np].fa = nq;
            while(p!=-1&&t[p].son[w]==q)
            {
                t[p].son[w] = nq;
                p = t[p].fa;
            }
        }
    }
    last = np;
}

int w[maxn], r[maxn*2];

void topsort()
{
    for(int i = 0; i <= l; ++i) w[i] = 0;
    for(int i = 1; i <= tots; ++i) w[t[i].len]++;
    for(int i = 1; i <= l; ++i) w[i] += w[i-1];
    for(int i = tots; i >= 1; --i) r[w[t[i].len]--] = i;
    r[0] = 0;
}

int dp[maxn*2];
char s[maxn];


int main()
{
    int n, k, p;
    int _;
    cin >> _;
    while(_--)
    {
        scanf("%d %s", &k, &s);
        int tl = strlen(s);
        l = 0;
        sam_init();
        for(int i = 0; i < tl; ++i)
        {
            ++l;
            extend(s[i]-'a');
        }
        for(int i = 0; i <= tots; ++i) dp[i] = 0;
        topsort();
        p = root;
        for(int i = 0; i < l; ++i)
        {
            p = t[p].son[s[i]-'a'];
            dp[p]++;
        }
        for(int i = tots; i >= 1; --i)
        {
            p = r[i];
            if(t[p].fa != -1) dp[t[p].fa] += dp[p];
        }
        ll ans1 = 0, ans2 = 0;
        for(int i = 1; i <= tots; ++i)
            if(dp[i] >= k)
                ans1 += t[i].len-t[t[i].fa].len;
        for(int i = 1; i <= tots; ++i)
            if(dp[i] >= k+1)
                ans2 += t[i].len-t[t[i].fa].len;
        printf("%lld\n", ans1-ans2);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值