HDU6194-string string string

本文介绍了一种使用后缀数组和线段树解决字符串中子串出现次数问题的方法。通过构建后缀数组并结合线段树进行区间查询,有效地解决了寻找特定出现次数的子串数量的问题。

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

string string string

                                                                        Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
                                                                                                Total Submission(s): 1464    Accepted Submission(s): 422


Problem Description
Uncle Mao is a wonderful ACMER. One day he met an easy problem, but Uncle Mao was so lazy that he left the problem to you. I hope you can give him a solution.
Given a string s, we define a substring that happens exactly  k  times as an important string, and you need to find out how many substrings which are important strings.
 

Input
The first line contains an integer  T  ( T100 ) implying the number of test cases.
For each test case, there are two lines:
the first line contains an integer  k  ( k1 ) which is described above;
the second line contain a string  s  ( length(s)105 ).
It's guaranteed that  length(s)2106 .
 

Output
For each test case, print the number of the important substrings in a line.
 

Sample Input
  
2 2 abcabc 3 abcabcabcabc
 

Sample Output
  
6 9
 

Source
 


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

解题思路:后缀数组+线段树


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <functional>

using namespace std;

#define LL long long
const int INF = 0x3f3f3f3f;
const int N = 200010;

struct Sa
{
    char s[N];
    int rk[2][N], sa[N], h[N], w[N], now, k, n;
    int rmq[N][20], lg[N], bel[N];
    int mi[N * 4];
    LL ans;

    bool GetS()
    {
        scanf("%d", &k);
        scanf("%s", s + 1);
        return true;
    }

    void getsa(int z, int &m)
    {
        int x = now, y = now ^= 1;
        for (int i = 1; i <= z; i++) rk[y][i] = n - i + 1;
        for (int i = 1, j = z; i <= n; i++)
            if (sa[i] > z) rk[y][++j] = sa[i] - z;
        for (int i = 1; i <= m; i++) w[i] = 0;
        for (int i = 1; i <= n; i++) w[rk[x][rk[y][i]]]++;
        for (int i = 1; i <= m; i++) w[i] += w[i - 1];
        for (int i = n; i >= 1; i--) sa[w[rk[x][rk[y][i]]]--] = rk[y][i];
        for (int i = m = 1; i <= n; i++)
        {
            int *a = rk[x] + sa[i], *b = rk[x] + sa[i - 1];
            rk[y][sa[i]] = *a == *b&&*(a + z) == *(b + z) ? m - 1 : m++;
        }
    }

    void getsa(int m)
    {
        n = strlen(s + 1);
        now = rk[1][0] = sa[0] = s[0] = 0;
        for (int i = 1; i <= m; i++) w[i] = 0;
        for (int i = 1; i <= n; i++) w[s[i]]++;
        for (int i = 1; i <= m; i++) rk[1][i] = rk[1][i - 1] + (bool)w[i];
        for (int i = 1; i <= m; i++) w[i] += w[i - 1];
        for (int i = 1; i <= n; i++) rk[0][i] = rk[1][s[i]];
        for (int i = 1; i <= n; i++) sa[w[s[i]]--] = i;
        rk[1][n + 1] = rk[0][n + 1] = 0;
        for (int x = 1, y = rk[1][m]; x <= n&&y <= n; x <<= 1) getsa(x, y);
        for (int i = 1, j = 0; i <= n; h[rk[now][i++]] = j ? j-- : j)
        {
            if (rk[now][i] == 1) continue;
            int k = n - max(sa[rk[now][i] - 1], i);
            while (j <= k&&s[sa[rk[now][i] - 1] + j] == s[i + j]) ++j;
        }
    }

    void getrmq()
    {
        h[n + 1] = h[1] = lg[1] = 0;
        for (int i = 2; i <= n; i++)
            rmq[i][0] = h[i], lg[i] = lg[i >> 1] + 1;
        for (int i = 1; (1 << i) <= n; i++)
        {
            for (int j = 2; j <= n; j++)
            {
                if (j + (1 << i) > n + 1) break;
                rmq[j][i] = min(rmq[j][i - 1], rmq[j + (1 << i - 1)][i - 1]);
            }
        }
    }

    int lcp(int x, int y)
    {
        int l = min(rk[now][x], rk[now][y]) + 1, r = max(rk[now][x], rk[now][y]);
        return min(rmq[l][lg[r - l + 1]], rmq[r - (1 << lg[r - l + 1]) + 1][lg[r - l + 1]]);
    }

    void build(int k, int l, int r)
    {
        if (l == r) { mi[k] = h[l]; return; }
        int mid = (l + r) >> 1;
        build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r);
        mi[k] = min(mi[k << 1], mi[k << 1 | 1]);
    }

    int query(int k, int l, int r, int ll, int rr)
    {
        if (l >= ll&&r <= rr) return mi[k];
        int mid = (l + r) >> 1, ans = INF;
        if (ll <= mid) ans = min(ans, query(k << 1, l, mid, ll, rr));
        if (rr > mid) ans = min(ans, query(k << 1 | 1, mid + 1, r, ll, rr));
        return ans;
    }

    void work()
    {
        getsa(300);
        build(1, 1, n);
        ans = 0;
        for (int i = 1; i + k - 1 <= n; i++)
        {
            int x = (k == 1 ? n - sa[i] + 1 : query(1, 1, n, i + 1, i + k - 1));
            int y = 0;
            y = max(y, h[i]);
            if (i + k - 1 != n) y = max(y, h[k + i]);
            if (x >= y) ans += 1LL * (x - y);
        }
        printf("%lld\n", ans);
    }
}sa;

int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        sa.GetS();
        sa.work();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值