题解
题目大意 给你一个字符串和AB 问这个串的所有不重复子串出现次数在AB之间(包括A和B)的有多少个
使用后缀自动机 计算每个节点的endpos的时候顺带计算答案 题目没有给单个串长度 可以认为是2e5
AC代码
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 2e5 + 10;
const int MAXC = 26;
int len[MAXN << 1], lnk[MAXN << 1], cnt[MAXN << 1], nxt[MAXN << 1][MAXC], idx, lst;
ll sub[MAXN << 1], ans;
char s[MAXN];
int a, b;
void Init()
{
lst = idx = 1;
lnk[1] = len[1] = 0;
}
void Extend(int c)
{
int x = ++idx;
sub[x] = 1; //可接受节点子串除后缀连接还需加一
len[x] = len[lst] + 1;
int p;
for (p = lst; p && !nxt[p][c]; p = lnk[p])
nxt[p][c] = x;
if (!p)
lnk[x] = 1, cnt[1]++;
else
{
int q = nxt[p][c];
if (len[p] + 1 == len[q])
lnk[x] = q, cnt[q]++;
else
{
int nq = ++idx;
len[nq] = len[p] + 1;
lnk[nq] = lnk[q]; //因后面lnk[q]改变此处不加cnt
memcpy(nxt[nq], nxt[q], sizeof(nxt[q]));
for (; p && nxt[p][c] == q; p = lnk[p])
nxt[p][c] = nq;
lnk[q] = lnk[x] = nq;
cnt[nq] += 2;
}
}
lst = x;
}
void GetCnt()
{
queue<int> q;
for (int i = 1; i <= idx; i++)
if (!cnt[i])
q.push(i);
while (!q.empty())
{
int x = q.front();
q.pop();
sub[lnk[x]] += sub[x];
if (--cnt[lnk[x]] == 0)
q.push(lnk[x]);
if (sub[x] >= a && sub[x] <= b) //出现次数在ab范围内
ans += len[x] - len[lnk[x]]; //该节点的子串数量
}
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
while (scanf("%s%d%d", s, &a, &b) != EOF)
{
memset(len, 0, sizeof(len));
memset(lnk, 0, sizeof(lnk));
memset(cnt, 0, sizeof(cnt));
memset(nxt, 0, sizeof(nxt));
memset(sub, 0, sizeof(sub));
ans = 0;
Init();
int n = strlen(s);
for (int i = 0; i < n; i++)
Extend(s[i] - 'A');
GetCnt();
cout << ans << endl;
}
return 0;
}
本文详细解析了一道关于字符串的编程题目,利用后缀自动机算法高效计算字符串所有不重复子串在特定频率范围内的数量。代码示例展示了如何初始化、扩展节点以及计算子串出现次数。
366

被折叠的 条评论
为什么被折叠?



