题解
题目大意 给你一个字符串和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;
}